appium 3.2.1 → 3.3.0
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/cli/args.d.ts +16 -12
- package/build/lib/cli/args.d.ts.map +1 -1
- package/build/lib/cli/args.js +15 -35
- package/build/lib/cli/args.js.map +1 -1
- package/build/lib/cli/driver-command.d.ts +51 -93
- package/build/lib/cli/driver-command.d.ts.map +1 -1
- package/build/lib/cli/driver-command.js +11 -66
- package/build/lib/cli/driver-command.js.map +1 -1
- package/build/lib/cli/extension-command.d.ts +211 -415
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +384 -653
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts +11 -16
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js +10 -28
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts +40 -69
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +24 -59
- package/build/lib/cli/parser.js.map +1 -1
- package/build/lib/cli/plugin-command.d.ts +50 -90
- package/build/lib/cli/plugin-command.d.ts.map +1 -1
- package/build/lib/cli/plugin-command.js +11 -63
- package/build/lib/cli/plugin-command.js.map +1 -1
- package/build/lib/cli/setup-command.d.ts +21 -26
- package/build/lib/cli/setup-command.d.ts.map +1 -1
- package/build/lib/cli/setup-command.js +13 -55
- package/build/lib/cli/setup-command.js.map +1 -1
- package/build/lib/cli/utils.d.ts +27 -29
- package/build/lib/cli/utils.d.ts.map +1 -1
- package/build/lib/cli/utils.js +29 -31
- package/build/lib/cli/utils.js.map +1 -1
- package/build/lib/config-file.d.ts +24 -67
- package/build/lib/config-file.d.ts.map +1 -1
- package/build/lib/config-file.js +56 -115
- package/build/lib/config-file.js.map +1 -1
- package/build/lib/config.d.ts +42 -44
- package/build/lib/config.d.ts.map +1 -1
- package/build/lib/config.js +75 -107
- package/build/lib/config.js.map +1 -1
- package/build/lib/constants.d.ts +23 -23
- package/build/lib/constants.d.ts.map +1 -1
- package/build/lib/constants.js +10 -15
- package/build/lib/constants.js.map +1 -1
- package/build/lib/doctor/doctor.d.ts +40 -57
- package/build/lib/doctor/doctor.d.ts.map +1 -1
- package/build/lib/doctor/doctor.js +29 -60
- package/build/lib/doctor/doctor.js.map +1 -1
- package/build/lib/grid-register.d.ts +32 -7
- package/build/lib/grid-register.d.ts.map +1 -1
- package/build/lib/grid-register.js +84 -48
- package/build/lib/grid-register.js.map +1 -1
- package/build/lib/logsink.d.ts +13 -22
- package/build/lib/logsink.d.ts.map +1 -1
- package/build/lib/logsink.js +48 -103
- package/build/lib/logsink.js.map +1 -1
- package/build/lib/main.js +1 -1
- package/build/lib/main.js.map +1 -1
- package/build/lib/schema/arg-spec.d.ts +32 -107
- package/build/lib/schema/arg-spec.d.ts.map +1 -1
- package/build/lib/schema/arg-spec.js +11 -107
- package/build/lib/schema/arg-spec.js.map +1 -1
- package/build/lib/schema/cli-args.d.ts +3 -15
- package/build/lib/schema/cli-args.d.ts.map +1 -1
- package/build/lib/schema/cli-args.js +15 -105
- package/build/lib/schema/cli-args.js.map +1 -1
- package/build/lib/schema/cli-transformers.d.ts +15 -12
- package/build/lib/schema/cli-transformers.d.ts.map +1 -1
- package/build/lib/schema/cli-transformers.js +15 -45
- package/build/lib/schema/cli-transformers.js.map +1 -1
- package/build/lib/schema/index.d.ts +2 -2
- package/build/lib/schema/index.d.ts.map +1 -1
- package/build/lib/schema/index.js.map +1 -1
- package/build/lib/schema/keywords.d.ts +12 -20
- package/build/lib/schema/keywords.d.ts.map +1 -1
- package/build/lib/schema/keywords.js +6 -51
- package/build/lib/schema/keywords.js.map +1 -1
- package/build/lib/schema/schema.d.ts +106 -231
- package/build/lib/schema/schema.d.ts.map +1 -1
- package/build/lib/schema/schema.js +75 -345
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils.d.ts +59 -238
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +55 -207
- package/build/lib/utils.js.map +1 -1
- package/lib/cli/{args.js → args.ts} +40 -51
- package/lib/cli/driver-command.ts +122 -0
- package/lib/cli/{extension-command.js → extension-command.ts} +610 -689
- package/lib/cli/extension.ts +65 -0
- package/lib/cli/{parser.js → parser.ts} +48 -71
- package/lib/cli/plugin-command.ts +117 -0
- package/lib/cli/{setup-command.js → setup-command.ts} +57 -72
- package/lib/cli/utils.ts +97 -0
- package/lib/config-file.ts +212 -0
- package/lib/{config.js → config.ts} +129 -141
- package/lib/{constants.js → constants.ts} +30 -41
- package/lib/doctor/{doctor.js → doctor.ts} +81 -91
- package/lib/grid-register.ts +250 -0
- package/lib/{logsink.js → logsink.ts} +91 -137
- package/lib/main.js +1 -1
- package/lib/schema/arg-spec.ts +131 -0
- package/lib/schema/cli-args.ts +171 -0
- package/lib/schema/cli-transformers.ts +83 -0
- package/lib/schema/keywords.ts +96 -0
- package/lib/schema/schema.ts +449 -0
- package/lib/utils.ts +404 -0
- package/package.json +19 -20
- package/tsconfig.json +1 -1
- package/build/package.json +0 -99
- package/lib/cli/driver-command.js +0 -174
- package/lib/cli/extension.js +0 -74
- package/lib/cli/plugin-command.js +0 -164
- package/lib/cli/utils.js +0 -91
- package/lib/config-file.js +0 -228
- package/lib/grid-register.js +0 -146
- package/lib/schema/arg-spec.js +0 -229
- package/lib/schema/cli-args.js +0 -254
- package/lib/schema/cli-transformers.js +0 -113
- package/lib/schema/keywords.js +0 -136
- package/lib/schema/schema.js +0 -725
- package/lib/utils.js +0 -512
- /package/lib/schema/{index.js → index.ts} +0 -0
|
@@ -60,19 +60,15 @@ class NoUpdatesAvailableError extends Error {
|
|
|
60
60
|
}
|
|
61
61
|
/**
|
|
62
62
|
* Omits `driverName`/`pluginName` props from the receipt to make a {@linkcode ExtManifest}
|
|
63
|
-
* @template {ExtensionType} ExtType
|
|
64
|
-
* @param {ExtInstallReceipt<ExtType>} receipt
|
|
65
|
-
* @returns {ExtManifest<ExtType>}
|
|
66
63
|
*/
|
|
67
64
|
function receiptToManifest(receipt) {
|
|
68
|
-
return
|
|
65
|
+
return lodash_1.default.omit(receipt, 'driverName', 'pluginName');
|
|
69
66
|
}
|
|
70
67
|
/**
|
|
71
68
|
* Fetches the remote extension version requirements
|
|
72
69
|
*
|
|
73
|
-
* @param
|
|
74
|
-
* @param
|
|
75
|
-
* @returns {Promise<[string, string|null]>}
|
|
70
|
+
* @param pkgName Extension name
|
|
71
|
+
* @param [pkgVer] Extension version (if not provided then the latest is assumed)
|
|
76
72
|
*/
|
|
77
73
|
async function getRemoteExtensionVersionReq(pkgName, pkgVer) {
|
|
78
74
|
const allDeps = await support_1.npm.getPackageInfo(`${pkgName}${pkgVer ? `@${pkgVer}` : ``}`, ['peerDependencies', 'dependencies']);
|
|
@@ -80,28 +76,24 @@ async function getRemoteExtensionVersionReq(pkgName, pkgVer) {
|
|
|
80
76
|
.find(([name]) => name === 'appium');
|
|
81
77
|
return [utils_2.npmPackage.version, requiredVersionPair ? requiredVersionPair[1] : null];
|
|
82
78
|
}
|
|
83
|
-
/**
|
|
84
|
-
* @template {ExtensionType} ExtType
|
|
85
|
-
*/
|
|
86
79
|
class ExtensionCliCommand {
|
|
87
80
|
/**
|
|
88
81
|
* This is the `DriverConfig` or `PluginConfig`, depending on `ExtType`.
|
|
89
|
-
* @type {ExtensionConfig<ExtType>}
|
|
90
82
|
*/
|
|
91
83
|
config;
|
|
92
84
|
/**
|
|
93
85
|
* {@linkcode Record} of official plugins or drivers.
|
|
94
|
-
* @type {KnownExtensions<ExtType>}
|
|
95
86
|
*/
|
|
96
87
|
knownExtensions;
|
|
97
88
|
/**
|
|
98
89
|
* If `true`, command output has been requested as JSON.
|
|
99
|
-
* @type {boolean}
|
|
100
90
|
*/
|
|
101
91
|
isJsonOutput;
|
|
92
|
+
log;
|
|
102
93
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
94
|
+
* Creates an extension command instance.
|
|
95
|
+
*
|
|
96
|
+
* @param opts - constructor options containing extension config and JSON mode
|
|
105
97
|
*/
|
|
106
98
|
constructor({ config, json }) {
|
|
107
99
|
this.config = config;
|
|
@@ -115,24 +107,10 @@ class ExtensionCliCommand {
|
|
|
115
107
|
return this.config.extensionType;
|
|
116
108
|
}
|
|
117
109
|
/**
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
* For TS to understand that a function throws an exception, it must actually throw an exception--
|
|
121
|
-
* in other words, _calling_ a function which is guaranteed to throw an exception is not enough--
|
|
122
|
-
* nor is something like `@returns {never}` which does not imply a thrown exception.
|
|
123
|
-
*
|
|
124
|
-
* @param {string} message
|
|
125
|
-
* @protected
|
|
126
|
-
* @throws {Error}
|
|
127
|
-
*/
|
|
128
|
-
_createFatalError(message) {
|
|
129
|
-
return new Error(this.log.decorate(message, 'error'));
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Take a CLI parse and run an extension command based on its type
|
|
110
|
+
* Executes an extension subcommand from parsed CLI args.
|
|
133
111
|
*
|
|
134
|
-
* @param
|
|
135
|
-
* @
|
|
112
|
+
* @param args - parsed CLI argument object
|
|
113
|
+
* @returns result of the executed extension subcommand
|
|
136
114
|
*/
|
|
137
115
|
async execute(args) {
|
|
138
116
|
const cmd = args[`${this.type}Command`];
|
|
@@ -143,11 +121,10 @@ class ExtensionCliCommand {
|
|
|
143
121
|
return await executeCmd(args);
|
|
144
122
|
}
|
|
145
123
|
/**
|
|
146
|
-
*
|
|
124
|
+
* Lists available/installed extensions and optional update metadata.
|
|
147
125
|
*
|
|
148
|
-
* @
|
|
149
|
-
* @
|
|
150
|
-
* @return {Promise<ExtensionList<ExtType>>} map of extension names to extension data
|
|
126
|
+
* @param opts - list command options
|
|
127
|
+
* @returns map of extension names to list data
|
|
151
128
|
*/
|
|
152
129
|
async list({ showInstalled, showUpdates, verbose = false }) {
|
|
153
130
|
const listData = this._buildListData(showInstalled);
|
|
@@ -166,284 +143,33 @@ class ExtensionCliCommand {
|
|
|
166
143
|
return await this._displayNormalListOutput(listData, showUpdates);
|
|
167
144
|
}
|
|
168
145
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* @template {ExtensionType} ExtType
|
|
172
|
-
* @param {boolean} showInstalled
|
|
173
|
-
* @returns {ExtensionList<ExtType>}
|
|
174
|
-
* @private
|
|
175
|
-
*/
|
|
176
|
-
_buildListData(showInstalled) {
|
|
177
|
-
const installedNames = Object.keys(this.config.installedExtensions);
|
|
178
|
-
const knownNames = Object.keys(this.knownExtensions);
|
|
179
|
-
return [...installedNames, ...knownNames].reduce((acc, name) => {
|
|
180
|
-
if (!acc[name]) {
|
|
181
|
-
if (installedNames.includes(name)) {
|
|
182
|
-
acc[name] = {
|
|
183
|
-
... /** @type {Partial<ExtManifest<ExtType>>} */(this.config.installedExtensions[name]),
|
|
184
|
-
installed: true,
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
else if (!showInstalled) {
|
|
188
|
-
acc[name] = /** @type {ExtensionListData<ExtType>} */ ({
|
|
189
|
-
pkgName: this.knownExtensions[name],
|
|
190
|
-
installed: false,
|
|
191
|
-
});
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
return acc;
|
|
195
|
-
}, /** @type {ExtensionList<ExtType>} */ ({}));
|
|
196
|
-
}
|
|
197
|
-
/**
|
|
198
|
-
* Check for available updates for installed extensions
|
|
199
|
-
*
|
|
200
|
-
* @template {ExtensionType} ExtType
|
|
201
|
-
* @param {ExtensionList<ExtType>} listData
|
|
202
|
-
* @param {boolean} showUpdates
|
|
203
|
-
* @param {string} lsMsg
|
|
204
|
-
* @returns {Promise<void>}
|
|
205
|
-
* @private
|
|
206
|
-
*/
|
|
207
|
-
async _checkForUpdates(listData, showUpdates, lsMsg) {
|
|
208
|
-
await (0, utils_1.spinWith)(this.isJsonOutput, lsMsg, async () => {
|
|
209
|
-
// We'd like to still show lsMsg even if showUpdates is false
|
|
210
|
-
if (!showUpdates) {
|
|
211
|
-
return;
|
|
212
|
-
}
|
|
213
|
-
// Filter to only extensions that need update checks (installed npm packages)
|
|
214
|
-
const extensionsToCheck = lodash_1.default.toPairs(listData).filter(([, data]) => data.installed && data.installType === extension_config_1.INSTALL_TYPE_NPM);
|
|
215
|
-
await bluebird_1.default.map(extensionsToCheck, async ([ext, data]) => {
|
|
216
|
-
try {
|
|
217
|
-
const updates = await this.checkForExtensionUpdate(ext);
|
|
218
|
-
data.updateVersion = updates.safeUpdate;
|
|
219
|
-
data.unsafeUpdateVersion = updates.unsafeUpdate;
|
|
220
|
-
data.upToDate = updates.safeUpdate === null && updates.unsafeUpdate === null;
|
|
221
|
-
}
|
|
222
|
-
catch (e) {
|
|
223
|
-
data.updateError = e.message;
|
|
224
|
-
}
|
|
225
|
-
}, { concurrency: MAX_CONCURRENT_REPO_FETCHES });
|
|
226
|
-
});
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* Add repository URLs to list data for all extensions
|
|
230
|
-
*
|
|
231
|
-
* @template {ExtensionType} ExtType
|
|
232
|
-
* @param {ExtensionList<ExtType>} listData
|
|
233
|
-
* @returns {Promise<void>}
|
|
234
|
-
* @private
|
|
235
|
-
*/
|
|
236
|
-
async _addRepositoryUrlsToListData(listData) {
|
|
237
|
-
await (0, utils_1.spinWith)(this.isJsonOutput, 'Fetching repository information', async () => {
|
|
238
|
-
await bluebird_1.default.map(lodash_1.default.values(listData), async (data) => {
|
|
239
|
-
const repoUrl = await this._getRepositoryUrl(data);
|
|
240
|
-
if (repoUrl) {
|
|
241
|
-
data.repositoryUrl = repoUrl;
|
|
242
|
-
}
|
|
243
|
-
}, { concurrency: MAX_CONCURRENT_REPO_FETCHES });
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
/**
|
|
247
|
-
* Display normal formatted output
|
|
248
|
-
*
|
|
249
|
-
* @template {ExtensionType} ExtType
|
|
250
|
-
* @param {ExtensionList<ExtType>} listData
|
|
251
|
-
* @param {boolean} showUpdates
|
|
252
|
-
* @returns {Promise<ExtensionList<ExtType>>}
|
|
253
|
-
* @private
|
|
254
|
-
*/
|
|
255
|
-
async _displayNormalListOutput(listData, showUpdates) {
|
|
256
|
-
for (const [name, data] of lodash_1.default.toPairs(listData)) {
|
|
257
|
-
const line = await this._formatExtensionLine(name, data, showUpdates);
|
|
258
|
-
this.log.log(line);
|
|
259
|
-
}
|
|
260
|
-
return listData;
|
|
261
|
-
}
|
|
262
|
-
/**
|
|
263
|
-
* Format a single extension line for display
|
|
264
|
-
*
|
|
265
|
-
* @template {ExtensionType} ExtType
|
|
266
|
-
* @param {string} name
|
|
267
|
-
* @param {ExtensionListData<ExtType>} data
|
|
268
|
-
* @param {boolean} showUpdates
|
|
269
|
-
* @returns {Promise<string>}
|
|
270
|
-
* @private
|
|
271
|
-
*/
|
|
272
|
-
async _formatExtensionLine(name, data, showUpdates) {
|
|
273
|
-
if (data.installed) {
|
|
274
|
-
const installTxt = this._formatInstallText(/** @type {InstalledExtensionListData<ExtType>} */ (data));
|
|
275
|
-
const updateTxt = showUpdates ? this._formatUpdateText(/** @type {InstalledExtensionListData<ExtType>} */ (data)) : '';
|
|
276
|
-
return `- ${name.yellow}${installTxt}${updateTxt}`;
|
|
277
|
-
}
|
|
278
|
-
const installTxt = ' [not installed]'.grey;
|
|
279
|
-
return `- ${name.yellow}${installTxt}`;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Format installation status text
|
|
283
|
-
*
|
|
284
|
-
* @template {ExtensionType} ExtType
|
|
285
|
-
* @param {InstalledExtensionListData<ExtType>} data
|
|
286
|
-
* @returns {string}
|
|
287
|
-
* @private
|
|
288
|
-
*/
|
|
289
|
-
_formatInstallText(data) {
|
|
290
|
-
const { installType, installSpec, version } = data;
|
|
291
|
-
let typeTxt;
|
|
292
|
-
switch (installType) {
|
|
293
|
-
case extension_config_1.INSTALL_TYPE_GIT:
|
|
294
|
-
case extension_config_1.INSTALL_TYPE_GITHUB:
|
|
295
|
-
typeTxt = `(cloned from ${installSpec})`.yellow;
|
|
296
|
-
break;
|
|
297
|
-
case extension_config_1.INSTALL_TYPE_LOCAL:
|
|
298
|
-
typeTxt = `(linked from ${installSpec})`.magenta;
|
|
299
|
-
break;
|
|
300
|
-
case extension_config_1.INSTALL_TYPE_DEV:
|
|
301
|
-
typeTxt = '(dev mode)';
|
|
302
|
-
break;
|
|
303
|
-
default:
|
|
304
|
-
typeTxt = '(npm)';
|
|
305
|
-
}
|
|
306
|
-
return `@${version.yellow} ${('[installed ' + typeTxt + ']').green}`;
|
|
307
|
-
}
|
|
308
|
-
/**
|
|
309
|
-
* Format update information text
|
|
310
|
-
*
|
|
311
|
-
* @template {ExtensionType} ExtType
|
|
312
|
-
* @param {InstalledExtensionListData<ExtType>} data
|
|
313
|
-
* @returns {string}
|
|
314
|
-
* @private
|
|
315
|
-
*/
|
|
316
|
-
_formatUpdateText(data) {
|
|
317
|
-
const { updateVersion, unsafeUpdateVersion, upToDate, updateError } = data;
|
|
318
|
-
if (updateError) {
|
|
319
|
-
return ` [Cannot check for updates: ${updateError}]`.red;
|
|
320
|
-
}
|
|
321
|
-
let txt = '';
|
|
322
|
-
if (updateVersion) {
|
|
323
|
-
txt += ` [${updateVersion} available]`.magenta;
|
|
324
|
-
}
|
|
325
|
-
if (upToDate) {
|
|
326
|
-
txt += ` [Up to date]`.green;
|
|
327
|
-
}
|
|
328
|
-
if (unsafeUpdateVersion) {
|
|
329
|
-
txt += ` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan;
|
|
330
|
-
}
|
|
331
|
-
return txt;
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Get repository URL from package data
|
|
335
|
-
*
|
|
336
|
-
* @template {ExtensionType} ExtType
|
|
337
|
-
* @param {ExtensionListData<ExtType>} data
|
|
338
|
-
* @returns {Promise<string|null>}
|
|
339
|
-
* @private
|
|
340
|
-
*/
|
|
341
|
-
async _getRepositoryUrl(data) {
|
|
342
|
-
if (data.installed && data.installPath) {
|
|
343
|
-
return await this._getRepositoryUrlFromInstalled(
|
|
344
|
-
/** @type {InstalledExtensionListData<ExtType>} */ (data));
|
|
345
|
-
}
|
|
346
|
-
if (data.pkgName && !data.installed) {
|
|
347
|
-
return await this._getRepositoryUrlFromNpm(data.pkgName);
|
|
348
|
-
}
|
|
349
|
-
return null;
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Get repository URL from installed extension's package.json
|
|
353
|
-
*
|
|
354
|
-
* @template {ExtensionType} ExtType
|
|
355
|
-
* @param {InstalledExtensionListData<ExtType>} data
|
|
356
|
-
* @returns {Promise<string|null>}
|
|
357
|
-
* @private
|
|
358
|
-
*/
|
|
359
|
-
async _getRepositoryUrlFromInstalled(data) {
|
|
360
|
-
try {
|
|
361
|
-
const pkgJsonPath = node_path_1.default.join(data.installPath, 'package.json');
|
|
362
|
-
if (await support_1.fs.exists(pkgJsonPath)) {
|
|
363
|
-
const pkg = JSON.parse(await support_1.fs.readFile(pkgJsonPath, 'utf8'));
|
|
364
|
-
if (pkg.repository) {
|
|
365
|
-
if (typeof pkg.repository === 'string') {
|
|
366
|
-
return pkg.repository;
|
|
367
|
-
}
|
|
368
|
-
if (pkg.repository.url) {
|
|
369
|
-
return pkg.repository.url.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
}
|
|
374
|
-
catch {
|
|
375
|
-
// Ignore errors reading package.json
|
|
376
|
-
}
|
|
377
|
-
return null;
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Get repository URL from npm for a package name
|
|
146
|
+
* Logs a message and returns an {@linkcode Error} to throw.
|
|
381
147
|
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*/
|
|
386
|
-
async _getRepositoryUrlFromNpm(pkgName) {
|
|
387
|
-
try {
|
|
388
|
-
const repoInfo = await support_1.npm.getPackageInfo(pkgName, ['repository']);
|
|
389
|
-
// When requesting only 'repository', npm.getPackageInfo returns the repository object directly
|
|
390
|
-
if (repoInfo) {
|
|
391
|
-
if (typeof repoInfo === 'string') {
|
|
392
|
-
return repoInfo;
|
|
393
|
-
}
|
|
394
|
-
if (repoInfo.url) {
|
|
395
|
-
return repoInfo.url.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
catch {
|
|
400
|
-
// Ignore errors fetching from npm
|
|
401
|
-
}
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
|
-
/**
|
|
405
|
-
* Checks whether the given extension is compatible with the currently installed server
|
|
148
|
+
* For TS to understand that a function throws an exception, it must actually throw an exception--
|
|
149
|
+
* in other words, _calling_ a function which is guaranteed to throw an exception is not enough--
|
|
150
|
+
* nor is something like a `never` return annotation, which does not imply a thrown exception.
|
|
406
151
|
*
|
|
407
|
-
* @
|
|
408
|
-
* @returns {Promise<void>}
|
|
152
|
+
* @throws {Error}
|
|
409
153
|
*/
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
await (0, utils_1.spinWith)(this.isJsonOutput, `Checking if '${pkgName}' is compatible`, async () => {
|
|
415
|
-
const [serverVersion, extVersionRequirement] = await getRemoteExtensionVersionReq(pkgName, pkgVer);
|
|
416
|
-
if (serverVersion && extVersionRequirement && !semver.satisfies(serverVersion, extVersionRequirement)) {
|
|
417
|
-
throw this._createFatalError(`'${installSpec}' cannot be installed because the server version it requires (${extVersionRequirement}) ` +
|
|
418
|
-
`does not meet the currently installed one (${serverVersion}). Please install ` +
|
|
419
|
-
`a compatible server version first.`);
|
|
420
|
-
}
|
|
421
|
-
});
|
|
154
|
+
_createFatalError(message) {
|
|
155
|
+
return new Error(this.log.decorate(message, 'error'));
|
|
422
156
|
}
|
|
423
157
|
/**
|
|
424
|
-
*
|
|
158
|
+
* Build the initial list data structure from installed and known extensions
|
|
425
159
|
*
|
|
426
|
-
* @param {InstallOpts} opts
|
|
427
|
-
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data
|
|
428
160
|
*/
|
|
429
161
|
async _install({ installSpec, installType, packageName }) {
|
|
430
|
-
/** @type {ExtInstallReceipt<ExtType>} */
|
|
431
|
-
let receipt;
|
|
432
162
|
if (packageName && [extension_config_1.INSTALL_TYPE_LOCAL, extension_config_1.INSTALL_TYPE_NPM].includes(installType)) {
|
|
433
163
|
throw this._createFatalError(`When using --source=${installType}, cannot also use --package`);
|
|
434
164
|
}
|
|
435
165
|
if (!packageName && [extension_config_1.INSTALL_TYPE_GIT, extension_config_1.INSTALL_TYPE_GITHUB].includes(installType)) {
|
|
436
166
|
throw this._createFatalError(`When using --source=${installType}, must also use --package`);
|
|
437
167
|
}
|
|
438
|
-
/**
|
|
439
|
-
* @type {InstallViaNpmArgs}
|
|
440
|
-
*/
|
|
441
168
|
let installViaNpmOpts;
|
|
442
169
|
/**
|
|
443
170
|
* The probable (?) name of the extension derived from the install spec.
|
|
444
171
|
*
|
|
445
172
|
* If using a local install type, this will remain empty.
|
|
446
|
-
* @type {string}
|
|
447
173
|
*/
|
|
448
174
|
let probableExtName = '';
|
|
449
175
|
// depending on `installType`, build the options to pass into `installViaNpm`
|
|
@@ -455,9 +181,9 @@ class ExtensionCliCommand {
|
|
|
455
181
|
installViaNpmOpts = {
|
|
456
182
|
installSpec,
|
|
457
183
|
installType,
|
|
458
|
-
pkgName:
|
|
184
|
+
pkgName: packageName,
|
|
459
185
|
};
|
|
460
|
-
probableExtName =
|
|
186
|
+
probableExtName = packageName;
|
|
461
187
|
}
|
|
462
188
|
else if (installType === extension_config_1.INSTALL_TYPE_GIT) {
|
|
463
189
|
// git urls can have '.git' at the end, but this is not necessary and would complicate the
|
|
@@ -466,12 +192,13 @@ class ExtensionCliCommand {
|
|
|
466
192
|
installViaNpmOpts = {
|
|
467
193
|
installSpec,
|
|
468
194
|
installType,
|
|
469
|
-
pkgName:
|
|
195
|
+
pkgName: packageName,
|
|
470
196
|
};
|
|
471
|
-
probableExtName =
|
|
197
|
+
probableExtName = packageName;
|
|
472
198
|
}
|
|
473
199
|
else {
|
|
474
|
-
let pkgName
|
|
200
|
+
let pkgName;
|
|
201
|
+
let pkgVer;
|
|
475
202
|
if (installType === extension_config_1.INSTALL_TYPE_LOCAL) {
|
|
476
203
|
pkgName = node_path_1.default.isAbsolute(installSpec) ? installSpec : node_path_1.default.resolve(installSpec);
|
|
477
204
|
}
|
|
@@ -522,11 +249,10 @@ class ExtensionCliCommand {
|
|
|
522
249
|
`installed ${this.type}s with "appium ${this.type} list --installed".`);
|
|
523
250
|
}
|
|
524
251
|
await this._checkInstallCompatibility(installViaNpmOpts);
|
|
525
|
-
receipt = await this.installViaNpm(installViaNpmOpts);
|
|
252
|
+
const receipt = await this.installViaNpm(installViaNpmOpts);
|
|
526
253
|
// this _should_ be the same as `probablyExtName` as the one derived above unless
|
|
527
254
|
// install type is local.
|
|
528
|
-
|
|
529
|
-
const extName = receipt[ /** @type {string} */(`${this.type}Name`)];
|
|
255
|
+
const extName = receipt[`${this.type}Name`];
|
|
530
256
|
// check _a second time_ with the more-accurate extName
|
|
531
257
|
if (this.config.isInstalled(extName)) {
|
|
532
258
|
throw this._createFatalError(`A ${this.type} named "${extName}" is already installed. ` +
|
|
@@ -535,7 +261,6 @@ class ExtensionCliCommand {
|
|
|
535
261
|
}
|
|
536
262
|
// this field does not exist as such in the manifest (it's used as a property name instead)
|
|
537
263
|
// so that's why it's being removed here.
|
|
538
|
-
/** @type {ExtManifest<ExtType>} */
|
|
539
264
|
const extManifest = receiptToManifest(receipt);
|
|
540
265
|
const [errors, warnings] = await bluebird_1.default.all([
|
|
541
266
|
this.config.getProblems(extName, extManifest),
|
|
@@ -561,146 +286,34 @@ class ExtensionCliCommand {
|
|
|
561
286
|
return this.config.installedExtensions;
|
|
562
287
|
}
|
|
563
288
|
/**
|
|
564
|
-
*
|
|
289
|
+
* Uninstall an extension.
|
|
290
|
+
*
|
|
291
|
+
* First tries to do this via `npm uninstall`, but if that fails, just `rm -rf`'s the extension dir.
|
|
565
292
|
*
|
|
566
|
-
*
|
|
567
|
-
*
|
|
293
|
+
* Will only remove the extension from the manifest if it has been successfully removed.
|
|
294
|
+
*
|
|
295
|
+
* @return map of all installed extension names to extension data (without the extension just uninstalled)
|
|
568
296
|
*/
|
|
569
|
-
async
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
// the string used for installation is either <name>@<ver> in the case of a standard NPM
|
|
573
|
-
// package, or whatever the user sent in otherwise.
|
|
574
|
-
const installStr = installType === extension_config_1.INSTALL_TYPE_NPM ? `${pkgName}${pkgVer ? `@${pkgVer}` : ''}` : installSpec;
|
|
575
|
-
const appiumHome = this.config.appiumHome;
|
|
576
|
-
try {
|
|
577
|
-
const { pkg, installPath } = await (0, utils_1.spinWith)(this.isJsonOutput, installMsg, async () => await support_1.npm.installPackage(appiumHome, installStr, { pkgName, installType }));
|
|
578
|
-
await (0, utils_1.spinWith)(this.isJsonOutput, validateMsg, async () => {
|
|
579
|
-
this.validatePackageJson(pkg, installSpec);
|
|
580
|
-
});
|
|
581
|
-
return this.getInstallationReceipt({
|
|
582
|
-
pkg,
|
|
583
|
-
installPath,
|
|
584
|
-
installType,
|
|
585
|
-
installSpec,
|
|
586
|
-
});
|
|
297
|
+
async _uninstall({ installSpec }) {
|
|
298
|
+
if (!this.config.isInstalled(installSpec)) {
|
|
299
|
+
throw this._createFatalError(`Can't uninstall ${this.type} '${installSpec}'; it is not installed`);
|
|
587
300
|
}
|
|
588
|
-
|
|
589
|
-
|
|
301
|
+
const extRecord = this.config.installedExtensions[installSpec];
|
|
302
|
+
if (extRecord.installType === extension_config_1.INSTALL_TYPE_DEV) {
|
|
303
|
+
this.log.warn(`Cannot uninstall ${this.type} "${installSpec}" because it is in development!`);
|
|
304
|
+
return this.config.installedExtensions;
|
|
590
305
|
}
|
|
306
|
+
const pkgName = extRecord.pkgName;
|
|
307
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, `Uninstalling ${this.type} '${installSpec}'`, async () => {
|
|
308
|
+
await support_1.npm.uninstallPackage(this.config.appiumHome, pkgName);
|
|
309
|
+
});
|
|
310
|
+
await this.config.removeExtension(installSpec);
|
|
311
|
+
this.log.ok(`Successfully uninstalled ${this.type} '${installSpec}'`.green);
|
|
312
|
+
return this.config.installedExtensions;
|
|
591
313
|
}
|
|
592
314
|
/**
|
|
593
|
-
*
|
|
594
|
-
* is designed to be overridden by drivers/plugins with their own particular text.
|
|
315
|
+
* Attempt to update one or more drivers using NPM
|
|
595
316
|
*
|
|
596
|
-
* @param {ExtensionArgs} args
|
|
597
|
-
* @returns {string}
|
|
598
|
-
*/
|
|
599
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
600
|
-
getPostInstallText(args) {
|
|
601
|
-
throw this._createFatalError('Must be implemented in final class');
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* Once a package is installed on-disk, this gathers some necessary metadata for validation.
|
|
605
|
-
*
|
|
606
|
-
* @param {GetInstallationReceiptOpts<ExtType>} opts
|
|
607
|
-
* @returns {ExtInstallReceipt<ExtType>}
|
|
608
|
-
*/
|
|
609
|
-
getInstallationReceipt({ pkg, installPath, installType, installSpec }) {
|
|
610
|
-
const { appium, name, version, peerDependencies } = pkg;
|
|
611
|
-
const strVersion = /** @type {string} */ (version);
|
|
612
|
-
/** @type {import('appium/types').InternalMetadata} */
|
|
613
|
-
const internal = {
|
|
614
|
-
pkgName: /** @type {string} */ (name),
|
|
615
|
-
version: strVersion,
|
|
616
|
-
installType,
|
|
617
|
-
installSpec,
|
|
618
|
-
installPath,
|
|
619
|
-
appiumVersion: peerDependencies?.appium,
|
|
620
|
-
};
|
|
621
|
-
/** @type {ExtMetadata<ExtType>} */
|
|
622
|
-
const extMetadata = appium;
|
|
623
|
-
return {
|
|
624
|
-
...internal,
|
|
625
|
-
...extMetadata,
|
|
626
|
-
};
|
|
627
|
-
}
|
|
628
|
-
/**
|
|
629
|
-
* Validates the _required_ root fields of an extension's `package.json` file.
|
|
630
|
-
*
|
|
631
|
-
* These required fields are:
|
|
632
|
-
* - `name`
|
|
633
|
-
* - `version`
|
|
634
|
-
* - `appium`
|
|
635
|
-
* @param {import('type-fest').PackageJson} pkg - `package.json` of extension
|
|
636
|
-
* @param {string} installSpec - Extension name/spec
|
|
637
|
-
* @throws {ReferenceError} If `package.json` has a missing or invalid field
|
|
638
|
-
* @returns {pkg is ExtPackageJson<ExtType>}
|
|
639
|
-
*/
|
|
640
|
-
validatePackageJson(pkg, installSpec) {
|
|
641
|
-
const { appium, name, version } = /** @type {ExtPackageJson<ExtType>} */ (pkg);
|
|
642
|
-
/**
|
|
643
|
-
*
|
|
644
|
-
* @param {string} field
|
|
645
|
-
* @returns {ReferenceError}
|
|
646
|
-
*/
|
|
647
|
-
const createMissingFieldError = (field) => new ReferenceError(`${this.type} "${installSpec}" invalid; missing a \`${field}\` field of its \`package.json\``);
|
|
648
|
-
if (!name) {
|
|
649
|
-
throw createMissingFieldError('name');
|
|
650
|
-
}
|
|
651
|
-
if (!version) {
|
|
652
|
-
throw createMissingFieldError('version');
|
|
653
|
-
}
|
|
654
|
-
if (!appium) {
|
|
655
|
-
throw createMissingFieldError('appium');
|
|
656
|
-
}
|
|
657
|
-
this.validateExtensionFields(appium, installSpec);
|
|
658
|
-
return true;
|
|
659
|
-
}
|
|
660
|
-
/**
|
|
661
|
-
* For any `package.json` fields which a particular type of extension requires, validate the
|
|
662
|
-
* presence and form of those fields on the `package.json` data, throwing an error if anything is
|
|
663
|
-
* amiss.
|
|
664
|
-
*
|
|
665
|
-
* @param {ExtMetadata<ExtType>} extMetadata - the data in the "appium" field of `package.json` for an extension
|
|
666
|
-
* @param {string} installSpec - Extension name/spec
|
|
667
|
-
*/
|
|
668
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
669
|
-
validateExtensionFields(extMetadata, installSpec) {
|
|
670
|
-
throw this._createFatalError('Must be implemented in final class');
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* Uninstall an extension.
|
|
674
|
-
*
|
|
675
|
-
* First tries to do this via `npm uninstall`, but if that fails, just `rm -rf`'s the extension dir.
|
|
676
|
-
*
|
|
677
|
-
* Will only remove the extension from the manifest if it has been successfully removed.
|
|
678
|
-
*
|
|
679
|
-
* @param {UninstallOpts} opts
|
|
680
|
-
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data (without the extension just uninstalled)
|
|
681
|
-
*/
|
|
682
|
-
async _uninstall({ installSpec }) {
|
|
683
|
-
if (!this.config.isInstalled(installSpec)) {
|
|
684
|
-
throw this._createFatalError(`Can't uninstall ${this.type} '${installSpec}'; it is not installed`);
|
|
685
|
-
}
|
|
686
|
-
const extRecord = this.config.installedExtensions[installSpec];
|
|
687
|
-
if (extRecord.installType === extension_config_1.INSTALL_TYPE_DEV) {
|
|
688
|
-
this.log.warn(`Cannot uninstall ${this.type} "${installSpec}" because it is in development!`);
|
|
689
|
-
return this.config.installedExtensions;
|
|
690
|
-
}
|
|
691
|
-
const pkgName = extRecord.pkgName;
|
|
692
|
-
await (0, utils_1.spinWith)(this.isJsonOutput, `Uninstalling ${this.type} '${installSpec}'`, async () => {
|
|
693
|
-
await support_1.npm.uninstallPackage(this.config.appiumHome, pkgName);
|
|
694
|
-
});
|
|
695
|
-
await this.config.removeExtension(installSpec);
|
|
696
|
-
this.log.ok(`Successfully uninstalled ${this.type} '${installSpec}'`.green);
|
|
697
|
-
return this.config.installedExtensions;
|
|
698
|
-
}
|
|
699
|
-
/**
|
|
700
|
-
* Attempt to update one or more drivers using NPM
|
|
701
|
-
*
|
|
702
|
-
* @param {ExtensionUpdateOpts} updateSpec
|
|
703
|
-
* @return {Promise<ExtensionUpdateResult>}
|
|
704
317
|
*/
|
|
705
318
|
async _update({ installSpec, unsafe }) {
|
|
706
319
|
const shouldUpdateAll = installSpec === UPDATE_ALL;
|
|
@@ -712,11 +325,9 @@ class ExtensionCliCommand {
|
|
|
712
325
|
? Object.keys(this.config.installedExtensions)
|
|
713
326
|
: [installSpec];
|
|
714
327
|
// 'errors' will have ext names as keys and error objects as values
|
|
715
|
-
/** @type {Record<string,Error>} */
|
|
716
328
|
const errors = {};
|
|
717
329
|
// 'updates' will have ext names as keys and update objects as values, where an update
|
|
718
330
|
// object is of the form {from: versionString, to: versionString}
|
|
719
|
-
/** @type {Record<string,UpdateReport>} */
|
|
720
331
|
const updates = {};
|
|
721
332
|
for (const e of extsToUpdate) {
|
|
722
333
|
try {
|
|
@@ -738,6 +349,9 @@ class ExtensionCliCommand {
|
|
|
738
349
|
`breaking changes. If you want to apply this update, re-run with --unsafe`);
|
|
739
350
|
}
|
|
740
351
|
const updateVer = unsafe && update.unsafeUpdate ? update.unsafeUpdate : update.safeUpdate;
|
|
352
|
+
if (!updateVer) {
|
|
353
|
+
throw new NoUpdatesAvailableError();
|
|
354
|
+
}
|
|
741
355
|
await (0, utils_1.spinWith)(this.isJsonOutput, `Updating ${this.type} '${e}' from ${update.current} to ${updateVer}`, async () => await this.updateExtension(e, updateVer));
|
|
742
356
|
// if we're doing a safe update, but an unsafe update is also available, let the user know
|
|
743
357
|
if (!unsafe && update.unsafeUpdate) {
|
|
@@ -774,15 +388,13 @@ class ExtensionCliCommand {
|
|
|
774
388
|
* Given an extension name, figure out what its highest possible version upgrade is, and also the
|
|
775
389
|
* highest possible safe upgrade.
|
|
776
390
|
*
|
|
777
|
-
* @param
|
|
778
|
-
* @return {Promise<PossibleUpdates>}
|
|
391
|
+
* @param ext - name of extension
|
|
779
392
|
*/
|
|
780
393
|
async checkForExtensionUpdate(ext) {
|
|
781
394
|
// TODO decide how we want to handle beta versions?
|
|
782
395
|
// this is a helper method, 'ext' is assumed to already be installed here, and of the npm
|
|
783
396
|
// install type
|
|
784
397
|
const { version, pkgName } = this.config.installedExtensions[ext];
|
|
785
|
-
/** @type {string?} */
|
|
786
398
|
let unsafeUpdate = await support_1.npm.getLatestVersion(this.config.appiumHome, pkgName);
|
|
787
399
|
let safeUpdate = await support_1.npm.getLatestSafeUpgradeVersion(this.config.appiumHome, pkgName, version);
|
|
788
400
|
if (unsafeUpdate !== null && !support_1.util.compareVersions(unsafeUpdate, '>', version)) {
|
|
@@ -800,46 +412,10 @@ class ExtensionCliCommand {
|
|
|
800
412
|
}
|
|
801
413
|
return { current: version, safeUpdate, unsafeUpdate };
|
|
802
414
|
}
|
|
803
|
-
/**
|
|
804
|
-
* Actually update an extension installed by NPM, using the NPM cli. And update the installation
|
|
805
|
-
* manifest.
|
|
806
|
-
*
|
|
807
|
-
* @param {string} installSpec - name of extension to update
|
|
808
|
-
* @param {string} version - version string identifier to update extension to
|
|
809
|
-
* @returns {Promise<void>}
|
|
810
|
-
*/
|
|
811
|
-
async updateExtension(installSpec, version) {
|
|
812
|
-
const { pkgName, installType } = this.config.installedExtensions[installSpec];
|
|
813
|
-
const extData = await this.installViaNpm({
|
|
814
|
-
installSpec,
|
|
815
|
-
installType,
|
|
816
|
-
pkgName,
|
|
817
|
-
pkgVer: version,
|
|
818
|
-
});
|
|
819
|
-
delete extData[ /** @type {string} */(`${this.type}Name`)];
|
|
820
|
-
await this.config.updateExtension(installSpec, extData);
|
|
821
|
-
}
|
|
822
|
-
/**
|
|
823
|
-
* Just wraps {@linkcode child_process.spawn} with some default options
|
|
824
|
-
*
|
|
825
|
-
* @param {string} cwd - CWD
|
|
826
|
-
* @param {string} script - Path to script
|
|
827
|
-
* @param {string[]} args - Extra args for script
|
|
828
|
-
* @param {import('child_process').SpawnOptions} opts - Options
|
|
829
|
-
* @returns {import('node:child_process').ChildProcess}
|
|
830
|
-
*/
|
|
831
|
-
_runUnbuffered(cwd, script, args = [], opts = {}) {
|
|
832
|
-
return (0, node_child_process_1.spawn)(process.execPath, [script, ...args], {
|
|
833
|
-
cwd,
|
|
834
|
-
stdio: 'inherit',
|
|
835
|
-
...opts,
|
|
836
|
-
});
|
|
837
|
-
}
|
|
838
415
|
/**
|
|
839
416
|
* Runs doctor checks for the given extension.
|
|
840
417
|
*
|
|
841
|
-
* @
|
|
842
|
-
* @returns {Promise<number>} The amount of Doctor checks that were
|
|
418
|
+
* @returns The amount of Doctor checks that were
|
|
843
419
|
* successfully loaded and executed for the given extension
|
|
844
420
|
* @throws {Error} If any of the mandatory Doctor checks fails.
|
|
845
421
|
*/
|
|
@@ -867,7 +443,8 @@ class ExtensionCliCommand {
|
|
|
867
443
|
throw this._createFatalError(`The 'doctor' entry in the package manifest '${packageJsonPath}' must be a proper object ` +
|
|
868
444
|
`containing the 'checks' key with the array of script paths`);
|
|
869
445
|
}
|
|
870
|
-
const paths = doctorSpec.checks
|
|
446
|
+
const paths = doctorSpec.checks
|
|
447
|
+
.map((p) => {
|
|
871
448
|
const scriptPath = node_path_1.default.resolve(moduleRoot, p);
|
|
872
449
|
if (!node_path_1.default.normalize(scriptPath).startsWith(node_path_1.default.normalize(moduleRoot))) {
|
|
873
450
|
this.log.error(`The doctor check script '${p}' from the package manifest '${packageJsonPath}' must be located ` +
|
|
@@ -875,8 +452,8 @@ class ExtensionCliCommand {
|
|
|
875
452
|
return null;
|
|
876
453
|
}
|
|
877
454
|
return scriptPath;
|
|
878
|
-
})
|
|
879
|
-
|
|
455
|
+
})
|
|
456
|
+
.filter((p) => Boolean(p));
|
|
880
457
|
const loadChecksPromises = [];
|
|
881
458
|
for (const p of paths) {
|
|
882
459
|
const promise = (async () => {
|
|
@@ -891,8 +468,7 @@ class ExtensionCliCommand {
|
|
|
891
468
|
})();
|
|
892
469
|
loadChecksPromises.push(promise);
|
|
893
470
|
}
|
|
894
|
-
const isDoctorCheck = (
|
|
895
|
-
/** @type {import('@appium/types').IDoctorCheck[]} */
|
|
471
|
+
const isDoctorCheck = (x) => ['diagnose', 'fix', 'hasAutofix', 'isOptional'].every((method) => lodash_1.default.isFunction(x?.[method]));
|
|
896
472
|
const checks = lodash_1.default.flatMap((await bluebird_1.default.all(loadChecksPromises)).filter(Boolean).map(lodash_1.default.toPairs))
|
|
897
473
|
.map(([, value]) => value)
|
|
898
474
|
.filter(isDoctorCheck);
|
|
@@ -916,10 +492,8 @@ class ExtensionCliCommand {
|
|
|
916
492
|
* `scripts` field is not a plain object, or if the `scriptName` is
|
|
917
493
|
* not found within `scripts` object.
|
|
918
494
|
*
|
|
919
|
-
* @param {RunOptions} opts
|
|
920
|
-
* @return {Promise<RunOutput>}
|
|
921
495
|
*/
|
|
922
|
-
async _run({ installSpec, scriptName, extraArgs = [], bufferOutput = false }) {
|
|
496
|
+
async _run({ installSpec, scriptName, extraArgs = [], bufferOutput = false, }) {
|
|
923
497
|
if (!this.config.isInstalled(installSpec)) {
|
|
924
498
|
throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
|
|
925
499
|
}
|
|
@@ -948,7 +522,7 @@ class ExtensionCliCommand {
|
|
|
948
522
|
this.log.ok(`Successfully retrieved the list of scripts`.green);
|
|
949
523
|
return {};
|
|
950
524
|
}
|
|
951
|
-
if (!(scriptName in
|
|
525
|
+
if (!(scriptName in extScripts)) {
|
|
952
526
|
throw this._createFatalError(`The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`);
|
|
953
527
|
}
|
|
954
528
|
const scriptPath = extScripts[scriptName];
|
|
@@ -1003,21 +577,335 @@ class ExtensionCliCommand {
|
|
|
1003
577
|
throw this._createFatalError(message);
|
|
1004
578
|
}
|
|
1005
579
|
}
|
|
580
|
+
_buildListData(showInstalled) {
|
|
581
|
+
const installedNames = Object.keys(this.config.installedExtensions);
|
|
582
|
+
const knownNames = Object.keys(this.knownExtensions);
|
|
583
|
+
return [...installedNames, ...knownNames].reduce((acc, name) => {
|
|
584
|
+
if (!acc[name]) {
|
|
585
|
+
if (installedNames.includes(name)) {
|
|
586
|
+
acc[name] = {
|
|
587
|
+
...this.config.installedExtensions[name],
|
|
588
|
+
installed: true,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
else if (!showInstalled) {
|
|
592
|
+
acc[name] = {
|
|
593
|
+
pkgName: this.knownExtensions[name],
|
|
594
|
+
installed: false,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
return acc;
|
|
599
|
+
}, {});
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Install an extension via NPM
|
|
603
|
+
*
|
|
604
|
+
*/
|
|
605
|
+
async installViaNpm({ installSpec, pkgName, pkgVer, installType, }) {
|
|
606
|
+
const installMsg = `Installing '${installSpec}'`;
|
|
607
|
+
const validateMsg = `Validating '${installSpec}'`;
|
|
608
|
+
// the string used for installation is either <name>@<ver> in the case of a standard NPM
|
|
609
|
+
// package, or whatever the user sent in otherwise.
|
|
610
|
+
const installStr = installType === extension_config_1.INSTALL_TYPE_NPM ? `${pkgName}${pkgVer ? `@${pkgVer}` : ''}` : installSpec;
|
|
611
|
+
const appiumHome = this.config.appiumHome;
|
|
612
|
+
try {
|
|
613
|
+
const { pkg, installPath } = await (0, utils_1.spinWith)(this.isJsonOutput, installMsg, async () => await support_1.npm.installPackage(appiumHome, installStr, { pkgName, installType }));
|
|
614
|
+
const validatedPkg = await (0, utils_1.spinWith)(this.isJsonOutput, validateMsg, async () => this.validatePackageJson(pkg, installSpec));
|
|
615
|
+
return this.getInstallationReceipt({
|
|
616
|
+
pkg: validatedPkg,
|
|
617
|
+
installPath,
|
|
618
|
+
installType,
|
|
619
|
+
installSpec,
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
catch (err) {
|
|
623
|
+
throw this._createFatalError(`Encountered an error when installing package: ${err.message}`);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Actually update an extension installed by NPM, using the NPM cli. And update the installation
|
|
628
|
+
* manifest.
|
|
629
|
+
*
|
|
630
|
+
* @param installSpec - name of extension to update
|
|
631
|
+
* @param version - version string identifier to update extension to
|
|
632
|
+
*/
|
|
633
|
+
async updateExtension(installSpec, version) {
|
|
634
|
+
const { pkgName, installType } = this.config.installedExtensions[installSpec];
|
|
635
|
+
const extData = await this.installViaNpm({
|
|
636
|
+
installSpec,
|
|
637
|
+
installType,
|
|
638
|
+
pkgName,
|
|
639
|
+
pkgVer: version,
|
|
640
|
+
});
|
|
641
|
+
delete extData[`${this.type}Name`];
|
|
642
|
+
await this.config.updateExtension(installSpec, extData);
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Just wraps {@linkcode child_process.spawn} with some default options
|
|
646
|
+
*
|
|
647
|
+
* @param cwd - CWD
|
|
648
|
+
* @param script - Path to script
|
|
649
|
+
* @param args - Extra args for script
|
|
650
|
+
* @param opts - Options
|
|
651
|
+
*/
|
|
652
|
+
_runUnbuffered(cwd, script, args = [], opts = {}) {
|
|
653
|
+
return (0, node_child_process_1.spawn)(process.execPath, [script, ...args], {
|
|
654
|
+
cwd,
|
|
655
|
+
stdio: 'inherit',
|
|
656
|
+
...opts,
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Once a package is installed on-disk, this gathers some necessary metadata for validation.
|
|
661
|
+
*
|
|
662
|
+
*/
|
|
663
|
+
getInstallationReceipt({ pkg, installPath, installType, installSpec, }) {
|
|
664
|
+
const { appium, name, version, peerDependencies } = pkg;
|
|
665
|
+
const strVersion = version;
|
|
666
|
+
const internal = {
|
|
667
|
+
pkgName: name,
|
|
668
|
+
version: strVersion,
|
|
669
|
+
installType,
|
|
670
|
+
installSpec,
|
|
671
|
+
installPath,
|
|
672
|
+
appiumVersion: peerDependencies?.appium,
|
|
673
|
+
};
|
|
674
|
+
const extMetadata = appium;
|
|
675
|
+
return {
|
|
676
|
+
...internal,
|
|
677
|
+
...extMetadata,
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Validates the _required_ root fields of an extension's `package.json` file.
|
|
682
|
+
*
|
|
683
|
+
* These required fields are:
|
|
684
|
+
* - `name`
|
|
685
|
+
* - `version`
|
|
686
|
+
* - `appium`
|
|
687
|
+
* @param pkg - `package.json` of extension
|
|
688
|
+
* @param installSpec - Extension name/spec
|
|
689
|
+
* @throws {ReferenceError} If `package.json` has a missing or invalid field
|
|
690
|
+
*/
|
|
691
|
+
validatePackageJson(pkg, installSpec) {
|
|
692
|
+
const { appium, name, version } = pkg;
|
|
693
|
+
const createMissingFieldError = (field) => new ReferenceError(`${this.type} "${installSpec}" invalid; missing a \`${field}\` field of its \`package.json\``);
|
|
694
|
+
if (!name) {
|
|
695
|
+
throw createMissingFieldError('name');
|
|
696
|
+
}
|
|
697
|
+
if (!version) {
|
|
698
|
+
throw createMissingFieldError('version');
|
|
699
|
+
}
|
|
700
|
+
if (!appium) {
|
|
701
|
+
throw createMissingFieldError('appium');
|
|
702
|
+
}
|
|
703
|
+
this.validateExtensionFields(appium, installSpec);
|
|
704
|
+
return pkg;
|
|
705
|
+
}
|
|
706
|
+
/**
|
|
707
|
+
* Check for available updates for installed extensions
|
|
708
|
+
*
|
|
709
|
+
*/
|
|
710
|
+
async _checkForUpdates(listData, showUpdates, lsMsg) {
|
|
711
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, lsMsg, async () => {
|
|
712
|
+
// We'd like to still show lsMsg even if showUpdates is false
|
|
713
|
+
if (!showUpdates) {
|
|
714
|
+
return;
|
|
715
|
+
}
|
|
716
|
+
// Filter to only extensions that need update checks (installed npm packages)
|
|
717
|
+
const extensionsToCheck = lodash_1.default.toPairs(listData).filter(([, data]) => data.installed && data.installType === extension_config_1.INSTALL_TYPE_NPM);
|
|
718
|
+
await bluebird_1.default.map(extensionsToCheck, async ([ext, data]) => {
|
|
719
|
+
try {
|
|
720
|
+
const updates = await this.checkForExtensionUpdate(ext);
|
|
721
|
+
data.updateVersion = updates.safeUpdate;
|
|
722
|
+
data.unsafeUpdateVersion = updates.unsafeUpdate;
|
|
723
|
+
data.upToDate = updates.safeUpdate === null && updates.unsafeUpdate === null;
|
|
724
|
+
}
|
|
725
|
+
catch (e) {
|
|
726
|
+
data.updateError = e.message;
|
|
727
|
+
}
|
|
728
|
+
}, { concurrency: MAX_CONCURRENT_REPO_FETCHES });
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
/**
|
|
732
|
+
* Add repository URLs to list data for all extensions
|
|
733
|
+
*
|
|
734
|
+
*/
|
|
735
|
+
async _addRepositoryUrlsToListData(listData) {
|
|
736
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, 'Fetching repository information', async () => {
|
|
737
|
+
await bluebird_1.default.map(lodash_1.default.values(listData), async (data) => {
|
|
738
|
+
const repoUrl = await this._getRepositoryUrl(data);
|
|
739
|
+
if (repoUrl) {
|
|
740
|
+
data.repositoryUrl = repoUrl;
|
|
741
|
+
}
|
|
742
|
+
}, { concurrency: MAX_CONCURRENT_REPO_FETCHES });
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Display normal formatted output
|
|
747
|
+
*
|
|
748
|
+
*/
|
|
749
|
+
async _displayNormalListOutput(listData, showUpdates) {
|
|
750
|
+
for (const [name, data] of lodash_1.default.toPairs(listData)) {
|
|
751
|
+
const line = await this._formatExtensionLine(name, data, showUpdates);
|
|
752
|
+
this.log.log(line);
|
|
753
|
+
}
|
|
754
|
+
return listData;
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* Format a single extension line for display
|
|
758
|
+
*
|
|
759
|
+
*/
|
|
760
|
+
async _formatExtensionLine(name, data, showUpdates) {
|
|
761
|
+
if (data.installed) {
|
|
762
|
+
const installTxt = this._formatInstallText(data);
|
|
763
|
+
const updateTxt = showUpdates ? this._formatUpdateText(data) : '';
|
|
764
|
+
return `- ${name.yellow}${installTxt}${updateTxt}`;
|
|
765
|
+
}
|
|
766
|
+
const installTxt = ' [not installed]'.grey;
|
|
767
|
+
return `- ${name.yellow}${installTxt}`;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* Format installation status text
|
|
771
|
+
*
|
|
772
|
+
*/
|
|
773
|
+
_formatInstallText(data) {
|
|
774
|
+
const { installType, installSpec, version } = data;
|
|
775
|
+
let typeTxt;
|
|
776
|
+
switch (installType) {
|
|
777
|
+
case extension_config_1.INSTALL_TYPE_GIT:
|
|
778
|
+
case extension_config_1.INSTALL_TYPE_GITHUB:
|
|
779
|
+
typeTxt = `(cloned from ${installSpec})`.yellow;
|
|
780
|
+
break;
|
|
781
|
+
case extension_config_1.INSTALL_TYPE_LOCAL:
|
|
782
|
+
typeTxt = `(linked from ${installSpec})`.magenta;
|
|
783
|
+
break;
|
|
784
|
+
case extension_config_1.INSTALL_TYPE_DEV:
|
|
785
|
+
typeTxt = '(dev mode)';
|
|
786
|
+
break;
|
|
787
|
+
default:
|
|
788
|
+
typeTxt = '(npm)';
|
|
789
|
+
}
|
|
790
|
+
return `@${String(version).yellow} ${('[installed ' + typeTxt + ']').green}`;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Format update information text
|
|
794
|
+
*
|
|
795
|
+
*/
|
|
796
|
+
_formatUpdateText(data) {
|
|
797
|
+
const { updateVersion, unsafeUpdateVersion, upToDate, updateError } = data;
|
|
798
|
+
if (updateError) {
|
|
799
|
+
return ` [Cannot check for updates: ${updateError}]`.red;
|
|
800
|
+
}
|
|
801
|
+
let txt = '';
|
|
802
|
+
if (updateVersion) {
|
|
803
|
+
txt += ` [${updateVersion} available]`.magenta;
|
|
804
|
+
}
|
|
805
|
+
if (upToDate) {
|
|
806
|
+
txt += ` [Up to date]`.green;
|
|
807
|
+
}
|
|
808
|
+
if (unsafeUpdateVersion) {
|
|
809
|
+
txt += ` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan;
|
|
810
|
+
}
|
|
811
|
+
return txt;
|
|
812
|
+
}
|
|
813
|
+
/**
|
|
814
|
+
* Get repository URL from package data
|
|
815
|
+
*
|
|
816
|
+
*/
|
|
817
|
+
async _getRepositoryUrl(data) {
|
|
818
|
+
if (data.installed && data.installPath) {
|
|
819
|
+
return await this._getRepositoryUrlFromInstalled(data);
|
|
820
|
+
}
|
|
821
|
+
if (data.pkgName && !data.installed) {
|
|
822
|
+
return await this._getRepositoryUrlFromNpm(data.pkgName);
|
|
823
|
+
}
|
|
824
|
+
return null;
|
|
825
|
+
}
|
|
826
|
+
/**
|
|
827
|
+
* Get repository URL from installed extension's package.json
|
|
828
|
+
*
|
|
829
|
+
*/
|
|
830
|
+
async _getRepositoryUrlFromInstalled(data) {
|
|
831
|
+
try {
|
|
832
|
+
const pkgJsonPath = node_path_1.default.join(String(data.installPath), 'package.json');
|
|
833
|
+
if (await support_1.fs.exists(pkgJsonPath)) {
|
|
834
|
+
const pkg = JSON.parse(await support_1.fs.readFile(pkgJsonPath, 'utf8'));
|
|
835
|
+
if (pkg.repository) {
|
|
836
|
+
if (typeof pkg.repository === 'string') {
|
|
837
|
+
return pkg.repository;
|
|
838
|
+
}
|
|
839
|
+
if (pkg.repository.url) {
|
|
840
|
+
return pkg.repository.url.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
catch {
|
|
846
|
+
// Ignore errors reading package.json
|
|
847
|
+
}
|
|
848
|
+
return null;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* Get repository URL from npm for a package name
|
|
852
|
+
*
|
|
853
|
+
*/
|
|
854
|
+
async _getRepositoryUrlFromNpm(pkgName) {
|
|
855
|
+
try {
|
|
856
|
+
const repoInfo = await support_1.npm.getPackageInfo(pkgName, ['repository']);
|
|
857
|
+
// When requesting only 'repository', npm.getPackageInfo returns the repository object directly
|
|
858
|
+
if (repoInfo) {
|
|
859
|
+
if (typeof repoInfo === 'string') {
|
|
860
|
+
return repoInfo;
|
|
861
|
+
}
|
|
862
|
+
if (repoInfo.url) {
|
|
863
|
+
return repoInfo.url.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
catch {
|
|
868
|
+
// Ignore errors fetching from npm
|
|
869
|
+
}
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
/**
|
|
873
|
+
* Checks whether the given extension is compatible with the currently installed server
|
|
874
|
+
*
|
|
875
|
+
*/
|
|
876
|
+
async _checkInstallCompatibility({ installSpec, pkgName, pkgVer, installType, }) {
|
|
877
|
+
if (extension_config_1.INSTALL_TYPE_NPM !== installType) {
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, `Checking if '${pkgName}' is compatible`, async () => {
|
|
881
|
+
const [serverVersion, extVersionRequirement] = await getRemoteExtensionVersionReq(pkgName, pkgVer);
|
|
882
|
+
if (serverVersion && extVersionRequirement && !semver.satisfies(serverVersion, extVersionRequirement)) {
|
|
883
|
+
throw this._createFatalError(`'${installSpec}' cannot be installed because the server version it requires (${extVersionRequirement}) ` +
|
|
884
|
+
`does not meet the currently installed one (${serverVersion}). Please install ` +
|
|
885
|
+
`a compatible server version first.`);
|
|
886
|
+
}
|
|
887
|
+
});
|
|
888
|
+
}
|
|
1006
889
|
}
|
|
1007
890
|
exports.ExtensionCommand = ExtensionCliCommand;
|
|
1008
891
|
/**
|
|
1009
892
|
* This is needed to ensure proper module resolution for installed extensions,
|
|
1010
893
|
* especially ESM ones.
|
|
1011
894
|
*
|
|
1012
|
-
* @param
|
|
1013
|
-
* @param
|
|
1014
|
-
* @param
|
|
895
|
+
* @param driverConfig - active driver extension config
|
|
896
|
+
* @param pluginConfig - active plugin extension config
|
|
897
|
+
* @param logger - logger instance used for non-fatal symlink errors
|
|
898
|
+
* @returns resolves when symlink injection has completed for all extensions
|
|
1015
899
|
*/
|
|
1016
900
|
async function injectAppiumSymlinks(driverConfig, pluginConfig, logger) {
|
|
1017
|
-
const
|
|
901
|
+
const isNpmInstalledExtension = (details) => details.installType === extension_config_1.INSTALL_TYPE_NPM && Boolean(details.installPath);
|
|
902
|
+
const installedExtensions = [
|
|
1018
903
|
...Object.values(driverConfig.installedExtensions || {}),
|
|
1019
|
-
...Object.values(pluginConfig.installedExtensions || {})
|
|
1020
|
-
]
|
|
904
|
+
...Object.values(pluginConfig.installedExtensions || {}),
|
|
905
|
+
];
|
|
906
|
+
const installPaths = lodash_1.default.compact(installedExtensions
|
|
907
|
+
.filter((details) => Boolean(details))
|
|
908
|
+
.filter(isNpmInstalledExtension)
|
|
1021
909
|
.map((details) => details.installPath));
|
|
1022
910
|
// After the extension is installed, we try to inject the appium module symlink
|
|
1023
911
|
// into the extension's node_modules folder if it is not there yet.
|
|
@@ -1030,12 +918,10 @@ async function injectAppiumSymlinks(driverConfig, pluginConfig, logger) {
|
|
|
1030
918
|
* This is needed to ensure proper module resolution for installed extensions,
|
|
1031
919
|
* especially ESM ones.
|
|
1032
920
|
*
|
|
1033
|
-
* @param
|
|
1034
|
-
* @param {import('@appium/types').AppiumLogger} logger
|
|
1035
|
-
* @returns {Promise<void>}
|
|
921
|
+
* @param dstFolder The destination folder where the symlink should be created
|
|
1036
922
|
*/
|
|
1037
923
|
async function injectAppiumSymlink(dstFolder, logger) {
|
|
1038
|
-
let appiumModuleRoot;
|
|
924
|
+
let appiumModuleRoot = '';
|
|
1039
925
|
try {
|
|
1040
926
|
appiumModuleRoot = (0, utils_2.getAppiumModuleRoot)();
|
|
1041
927
|
const symlinkPath = node_path_1.default.join(dstFolder, node_path_1.default.basename(appiumModuleRoot));
|
|
@@ -1050,159 +936,4 @@ async function injectAppiumSymlink(dstFolder, logger) {
|
|
|
1050
936
|
}
|
|
1051
937
|
}
|
|
1052
938
|
exports.default = ExtensionCliCommand;
|
|
1053
|
-
/**
|
|
1054
|
-
* Options for the {@linkcode ExtensionCliCommand} constructor
|
|
1055
|
-
* @template {ExtensionType} ExtType
|
|
1056
|
-
* @typedef ExtensionCommandOptions
|
|
1057
|
-
* @property {ExtensionConfig<ExtType>} config - the `DriverConfig` or `PluginConfig` instance used for this command
|
|
1058
|
-
* @property {boolean} json - whether the output of this command should be JSON or text
|
|
1059
|
-
*/
|
|
1060
|
-
/**
|
|
1061
|
-
* Extra stuff about extensions; used indirectly by {@linkcode ExtensionCliCommand.list}.
|
|
1062
|
-
*
|
|
1063
|
-
* @typedef ExtensionListMetadata
|
|
1064
|
-
* @property {boolean} installed - If `true`, the extension is installed
|
|
1065
|
-
* @property {boolean} upToDate - If the extension is installed and the latest
|
|
1066
|
-
* @property {string|null} updateVersion - If the extension is installed, the version it can be updated to
|
|
1067
|
-
* @property {string|null} unsafeUpdateVersion - Same as above, but a major version bump
|
|
1068
|
-
* @property {string} [updateError] - Update check error message (if present)
|
|
1069
|
-
* @property {boolean} [devMode] - If Appium is run from an extension's working copy
|
|
1070
|
-
* @property {string} [repositoryUrl] - Repository URL for the extension (if available)
|
|
1071
|
-
*/
|
|
1072
|
-
/**
|
|
1073
|
-
* @typedef {import('@appium/types').ExtensionType} ExtensionType
|
|
1074
|
-
* @typedef {import('@appium/types').DriverType} DriverType
|
|
1075
|
-
* @typedef {import('@appium/types').PluginType} PluginType
|
|
1076
|
-
*/
|
|
1077
|
-
/**
|
|
1078
|
-
* @template {ExtensionType} ExtType
|
|
1079
|
-
* @typedef {import('appium/types').ExtRecord<ExtType>} ExtRecord
|
|
1080
|
-
*/
|
|
1081
|
-
/**
|
|
1082
|
-
* @template {ExtensionType} ExtType
|
|
1083
|
-
* @typedef {import('../extension/extension-config').ExtensionConfig<ExtType>} ExtensionConfig
|
|
1084
|
-
*/
|
|
1085
|
-
/**
|
|
1086
|
-
* @template {ExtensionType} ExtType
|
|
1087
|
-
* @typedef {import('appium/types').ExtMetadata<ExtType>} ExtMetadata
|
|
1088
|
-
*/
|
|
1089
|
-
/**
|
|
1090
|
-
* @template {ExtensionType} ExtType
|
|
1091
|
-
* @typedef {import('appium/types').ExtManifest<ExtType>} ExtManifest
|
|
1092
|
-
*/
|
|
1093
|
-
/**
|
|
1094
|
-
* @template {ExtensionType} ExtType
|
|
1095
|
-
* @typedef {import('appium/types').ExtPackageJson<ExtType>} ExtPackageJson
|
|
1096
|
-
*/
|
|
1097
|
-
/**
|
|
1098
|
-
* @template {ExtensionType} ExtType
|
|
1099
|
-
* @typedef {import('appium/types').ExtInstallReceipt<ExtType>} ExtInstallReceipt
|
|
1100
|
-
*/
|
|
1101
|
-
/**
|
|
1102
|
-
* Possible return value for {@linkcode ExtensionCliCommand.list}
|
|
1103
|
-
* @template {ExtensionType} ExtType
|
|
1104
|
-
* @typedef {Partial<ExtManifest<ExtType>> & Partial<ExtensionListMetadata>} ExtensionListData
|
|
1105
|
-
*/
|
|
1106
|
-
/**
|
|
1107
|
-
* @template {ExtensionType} ExtType
|
|
1108
|
-
* @typedef {ExtManifest<ExtType> & ExtensionListMetadata} InstalledExtensionListData
|
|
1109
|
-
*/
|
|
1110
|
-
/**
|
|
1111
|
-
* Return value of {@linkcode ExtensionCliCommand.list}.
|
|
1112
|
-
* @template {ExtensionType} ExtType
|
|
1113
|
-
* @typedef {Record<string,ExtensionListData<ExtType>>} ExtensionList
|
|
1114
|
-
*/
|
|
1115
|
-
/**
|
|
1116
|
-
* Options for {@linkcode ExtensionCliCommand._run}.
|
|
1117
|
-
* @typedef RunOptions
|
|
1118
|
-
* @property {string} installSpec - name of the extension to run a script from
|
|
1119
|
-
* @property {string} [scriptName] - name of the script to run. If not provided
|
|
1120
|
-
* then all available script names will be printed
|
|
1121
|
-
* @property {string[]} [extraArgs] - arguments to pass to the script
|
|
1122
|
-
* @property {boolean} [bufferOutput] - if true, will buffer the output of the script and return it
|
|
1123
|
-
*/
|
|
1124
|
-
/**
|
|
1125
|
-
* Options for {@linkcode ExtensionCliCommand.doctor}.
|
|
1126
|
-
* @typedef DoctorOptions
|
|
1127
|
-
* @property {string} installSpec - name of the extension to run doctor checks for
|
|
1128
|
-
*/
|
|
1129
|
-
/**
|
|
1130
|
-
* Return value of {@linkcode ExtensionCliCommand._run}
|
|
1131
|
-
*
|
|
1132
|
-
* @typedef RunOutput
|
|
1133
|
-
* @property {string[]} [output] - script output if `bufferOutput` was `true` in {@linkcode RunOptions}
|
|
1134
|
-
*/
|
|
1135
|
-
/**
|
|
1136
|
-
* Options for {@linkcode ExtensionCliCommand._update}.
|
|
1137
|
-
* @typedef ExtensionUpdateOpts
|
|
1138
|
-
* @property {string} installSpec - the name of the extension to update
|
|
1139
|
-
* @property {boolean} unsafe - if true, will perform unsafe updates past major revision boundaries
|
|
1140
|
-
*/
|
|
1141
|
-
/**
|
|
1142
|
-
* Return value of {@linkcode ExtensionCliCommand._update}.
|
|
1143
|
-
* @typedef ExtensionUpdateResult
|
|
1144
|
-
* @property {Record<string,Error>} errors - map of ext names to error objects
|
|
1145
|
-
* @property {Record<string,UpdateReport>} updates - map of ext names to {@linkcode UpdateReport}s
|
|
1146
|
-
*/
|
|
1147
|
-
/**
|
|
1148
|
-
* Part of result of {@linkcode ExtensionCliCommand._update}.
|
|
1149
|
-
* @typedef UpdateReport
|
|
1150
|
-
* @property {string} from - version the extension was updated from
|
|
1151
|
-
* @property {string} to - version the extension was updated to
|
|
1152
|
-
*/
|
|
1153
|
-
/**
|
|
1154
|
-
* Options for {@linkcode ExtensionCliCommand._uninstall}.
|
|
1155
|
-
* @typedef UninstallOpts
|
|
1156
|
-
* @property {string} installSpec - the name or spec of an extension to uninstall
|
|
1157
|
-
*/
|
|
1158
|
-
/**
|
|
1159
|
-
* Used by {@linkcode ExtensionCliCommand.getPostInstallText}
|
|
1160
|
-
* @typedef ExtensionArgs
|
|
1161
|
-
* @property {string} extName - the name of an extension
|
|
1162
|
-
* @property {object} extData - the data for an installed extension
|
|
1163
|
-
*/
|
|
1164
|
-
/**
|
|
1165
|
-
* Options for {@linkcode ExtensionCliCommand.installViaNpm}
|
|
1166
|
-
* @typedef InstallViaNpmArgs
|
|
1167
|
-
* @property {string} installSpec - the name or spec of an extension to install
|
|
1168
|
-
* @property {string} pkgName - the NPM package name of the extension
|
|
1169
|
-
* @property {import('appium/types').InstallType} installType - type of install
|
|
1170
|
-
* @property {string} [pkgVer] - the specific version of the NPM package
|
|
1171
|
-
*/
|
|
1172
|
-
/**
|
|
1173
|
-
* Object returned by {@linkcode ExtensionCliCommand.checkForExtensionUpdate}
|
|
1174
|
-
* @typedef PossibleUpdates
|
|
1175
|
-
* @property {string} current - current version
|
|
1176
|
-
* @property {string?} safeUpdate - version we can safely update to if it exists, or null
|
|
1177
|
-
* @property {string?} unsafeUpdate - version we can unsafely update to if it exists, or null
|
|
1178
|
-
*/
|
|
1179
|
-
/**
|
|
1180
|
-
* Options for {@linkcode ExtensionCliCommand._install}
|
|
1181
|
-
* @typedef InstallOpts
|
|
1182
|
-
* @property {string} installSpec - the name or spec of an extension to install
|
|
1183
|
-
* @property {InstallType} installType - how to install this extension. One of the INSTALL_TYPES
|
|
1184
|
-
* @property {string} [packageName] - for git/github installs, the extension node package name
|
|
1185
|
-
*/
|
|
1186
|
-
/**
|
|
1187
|
-
* @template {ExtensionType} ExtType
|
|
1188
|
-
* @typedef {ExtType extends DriverType ? typeof import('../constants').KNOWN_DRIVERS : ExtType extends PluginType ? typeof import('../constants').KNOWN_PLUGINS : never} KnownExtensions
|
|
1189
|
-
*/
|
|
1190
|
-
/**
|
|
1191
|
-
* @typedef ListOptions
|
|
1192
|
-
* @property {boolean} showInstalled - whether should show only installed extensions
|
|
1193
|
-
* @property {boolean} showUpdates - whether should show available updates
|
|
1194
|
-
* @property {boolean} [verbose] - whether to show additional data from the extension
|
|
1195
|
-
*/
|
|
1196
|
-
/**
|
|
1197
|
-
* Opts for {@linkcode ExtensionCliCommand.getInstallationReceipt}
|
|
1198
|
-
* @template {ExtensionType} ExtType
|
|
1199
|
-
* @typedef GetInstallationReceiptOpts
|
|
1200
|
-
* @property {string} installPath
|
|
1201
|
-
* @property {string} installSpec
|
|
1202
|
-
* @property {ExtPackageJson<ExtType>} pkg
|
|
1203
|
-
* @property {InstallType} installType
|
|
1204
|
-
*/
|
|
1205
|
-
/**
|
|
1206
|
-
* @typedef {import('appium/types').InstallType} InstallType
|
|
1207
|
-
*/
|
|
1208
939
|
//# sourceMappingURL=extension-command.js.map
|