appium 3.2.2 → 3.3.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/appium.d.ts +147 -205
- package/build/lib/appium.d.ts.map +1 -1
- package/build/lib/appium.js +169 -282
- package/build/lib/appium.js.map +1 -1
- package/build/lib/bidi-commands.d.ts.map +1 -1
- package/build/lib/bidi-commands.js +11 -11
- package/build/lib/bidi-commands.js.map +1 -1
- package/build/lib/bootstrap/appium-initializer.d.ts +21 -0
- package/build/lib/bootstrap/appium-initializer.d.ts.map +1 -0
- package/build/lib/bootstrap/appium-initializer.js +146 -0
- package/build/lib/bootstrap/appium-initializer.js.map +1 -0
- package/build/lib/bootstrap/appium-main-runner.d.ts +22 -0
- package/build/lib/bootstrap/appium-main-runner.d.ts.map +1 -0
- package/build/lib/bootstrap/appium-main-runner.js +109 -0
- package/build/lib/bootstrap/appium-main-runner.js.map +1 -0
- package/build/lib/bootstrap/config-file.d.ts +37 -0
- package/build/lib/bootstrap/config-file.d.ts.map +1 -0
- package/build/lib/{config-file.js → bootstrap/config-file.js} +62 -138
- package/build/lib/bootstrap/config-file.js.map +1 -0
- package/build/lib/bootstrap/grid-v3-register.d.ts +20 -0
- package/build/lib/bootstrap/grid-v3-register.d.ts.map +1 -0
- package/build/lib/bootstrap/grid-v3-register.js +185 -0
- package/build/lib/bootstrap/grid-v3-register.js.map +1 -0
- package/build/lib/bootstrap/init-types.d.ts +16 -0
- package/build/lib/bootstrap/init-types.d.ts.map +1 -0
- package/build/lib/bootstrap/init-types.js +3 -0
- package/build/lib/bootstrap/init-types.js.map +1 -0
- package/build/lib/bootstrap/main-helpers.d.ts +55 -0
- package/build/lib/bootstrap/main-helpers.d.ts.map +1 -0
- package/build/lib/bootstrap/main-helpers.js +187 -0
- package/build/lib/bootstrap/main-helpers.js.map +1 -0
- package/build/lib/bootstrap/node-helpers.d.ts +32 -0
- package/build/lib/bootstrap/node-helpers.d.ts.map +1 -0
- package/build/lib/bootstrap/node-helpers.js +201 -0
- package/build/lib/bootstrap/node-helpers.js.map +1 -0
- package/build/lib/bootstrap/startup-config.d.ts +22 -0
- package/build/lib/bootstrap/startup-config.d.ts.map +1 -0
- package/build/lib/bootstrap/startup-config.js +111 -0
- package/build/lib/bootstrap/startup-config.js.map +1 -0
- 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 +20 -40
- 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 +173 -377
- package/build/lib/cli/extension-command.d.ts.map +1 -1
- package/build/lib/cli/extension-command.js +387 -656
- package/build/lib/cli/extension-command.js.map +1 -1
- package/build/lib/cli/extension.d.ts +10 -15
- package/build/lib/cli/extension.d.ts.map +1 -1
- package/build/lib/cli/extension.js +15 -33
- package/build/lib/cli/extension.js.map +1 -1
- package/build/lib/cli/parser.d.ts +37 -66
- package/build/lib/cli/parser.d.ts.map +1 -1
- package/build/lib/cli/parser.js +69 -104
- 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 +19 -61
- package/build/lib/cli/setup-command.js.map +1 -1
- package/build/lib/cli/utils.d.ts +33 -35
- package/build/lib/cli/utils.d.ts.map +1 -1
- package/build/lib/cli/utils.js +48 -50
- package/build/lib/cli/utils.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 +31 -62
- package/build/lib/doctor/doctor.js.map +1 -1
- package/build/lib/extension/driver-config.d.ts +18 -77
- package/build/lib/extension/driver-config.d.ts.map +1 -1
- package/build/lib/extension/driver-config.js +37 -125
- package/build/lib/extension/driver-config.js.map +1 -1
- package/build/lib/extension/extension-config.d.ts +103 -210
- package/build/lib/extension/extension-config.d.ts.map +1 -1
- package/build/lib/extension/extension-config.js +180 -342
- package/build/lib/extension/extension-config.js.map +1 -1
- package/build/lib/extension/index.d.ts +12 -29
- package/build/lib/extension/index.d.ts.map +1 -1
- package/build/lib/extension/index.js +33 -75
- package/build/lib/extension/index.js.map +1 -1
- package/build/lib/extension/manifest-migrations.d.ts +3 -20
- package/build/lib/extension/manifest-migrations.d.ts.map +1 -1
- package/build/lib/extension/manifest-migrations.js +20 -101
- package/build/lib/extension/manifest-migrations.js.map +1 -1
- package/build/lib/extension/manifest.d.ts +61 -107
- package/build/lib/extension/manifest.d.ts.map +1 -1
- package/build/lib/extension/manifest.js +181 -356
- package/build/lib/extension/manifest.js.map +1 -1
- package/build/lib/extension/package-changed.d.ts +1 -3
- package/build/lib/extension/package-changed.d.ts.map +1 -1
- package/build/lib/extension/package-changed.js +8 -15
- package/build/lib/extension/package-changed.js.map +1 -1
- package/build/lib/extension/plugin-config.d.ts +10 -52
- package/build/lib/extension/plugin-config.d.ts.map +1 -1
- package/build/lib/extension/plugin-config.js +11 -63
- package/build/lib/extension/plugin-config.js.map +1 -1
- package/build/lib/helpers/build.d.ts +22 -0
- package/build/lib/helpers/build.d.ts.map +1 -0
- package/build/lib/helpers/build.js +109 -0
- package/build/lib/helpers/build.js.map +1 -0
- package/build/lib/helpers/capability.d.ts +38 -0
- package/build/lib/helpers/capability.d.ts.map +1 -0
- package/build/lib/helpers/capability.js +128 -0
- package/build/lib/helpers/capability.js.map +1 -0
- package/build/lib/helpers/network.d.ts +14 -0
- package/build/lib/helpers/network.d.ts.map +1 -0
- package/build/lib/helpers/network.js +35 -0
- package/build/lib/helpers/network.js.map +1 -0
- package/build/lib/insecure-features.js +6 -6
- package/build/lib/insecure-features.js.map +1 -1
- package/build/lib/inspector-commands.d.ts +6 -0
- package/build/lib/inspector-commands.d.ts.map +1 -1
- package/build/lib/inspector-commands.js +6 -0
- package/build/lib/inspector-commands.js.map +1 -1
- package/build/lib/logger.d.ts +2 -3
- package/build/lib/logger.d.ts.map +1 -1
- package/build/lib/logger.js +2 -3
- package/build/lib/logger.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.d.ts +15 -58
- package/build/lib/main.d.ts.map +1 -1
- package/build/lib/main.js +25 -425
- 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-guards.d.ts +34 -0
- package/build/lib/schema/cli-args-guards.d.ts.map +1 -0
- package/build/lib/schema/cli-args-guards.js +49 -0
- package/build/lib/schema/cli-args-guards.js.map +1 -0
- 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 +17 -107
- 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/format-errors.d.ts +28 -0
- package/build/lib/schema/format-errors.d.ts.map +1 -0
- package/build/lib/schema/format-errors.js +29 -0
- package/build/lib/schema/format-errors.js.map +1 -0
- package/build/lib/schema/index.d.ts +4 -2
- package/build/lib/schema/index.d.ts.map +1 -1
- package/build/lib/schema/index.js +2 -0
- 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 +88 -358
- package/build/lib/schema/schema.js.map +1 -1
- package/build/lib/utils.d.ts +7 -267
- package/build/lib/utils.d.ts.map +1 -1
- package/build/lib/utils.js +10 -409
- package/build/lib/utils.js.map +1 -1
- package/lib/{appium.js → appium.ts} +297 -341
- package/lib/bidi-commands.ts +10 -14
- package/lib/bootstrap/appium-initializer.ts +212 -0
- package/lib/bootstrap/appium-main-runner.ts +172 -0
- package/lib/bootstrap/config-file.ts +178 -0
- package/lib/bootstrap/grid-v3-register.ts +250 -0
- package/lib/bootstrap/init-types.ts +31 -0
- package/lib/bootstrap/main-helpers.ts +223 -0
- package/lib/bootstrap/node-helpers.ts +180 -0
- package/lib/bootstrap/startup-config.ts +143 -0
- package/lib/cli/{args.js → args.ts} +45 -56
- package/lib/cli/driver-command.ts +122 -0
- package/lib/cli/{extension-command.js → extension-command.ts} +827 -906
- package/lib/cli/extension.ts +65 -0
- package/lib/cli/{parser.js → parser.ts} +93 -116
- package/lib/cli/plugin-command.ts +117 -0
- package/lib/cli/{setup-command.js → setup-command.ts} +59 -74
- package/lib/cli/utils.ts +97 -0
- package/lib/{constants.js → constants.ts} +30 -41
- package/lib/doctor/{doctor.js → doctor.ts} +82 -92
- package/lib/extension/driver-config.ts +165 -0
- package/lib/extension/{extension-config.js → extension-config.ts} +291 -405
- package/lib/extension/index.ts +143 -0
- package/lib/extension/manifest-migrations.ts +57 -0
- package/lib/extension/manifest.ts +369 -0
- package/lib/extension/{package-changed.js → package-changed.ts} +9 -18
- package/lib/extension/plugin-config.ts +62 -0
- package/lib/helpers/build.ts +111 -0
- package/lib/helpers/capability.ts +171 -0
- package/lib/helpers/network.ts +30 -0
- package/lib/insecure-features.ts +1 -1
- package/lib/inspector-commands.ts +6 -1
- package/lib/{logger.js → logger.ts} +1 -2
- package/lib/{logsink.js → logsink.ts} +91 -137
- package/lib/main.ts +60 -0
- package/lib/schema/arg-spec.ts +131 -0
- package/lib/schema/cli-args-guards.ts +67 -0
- package/lib/schema/cli-args.ts +171 -0
- package/lib/schema/cli-transformers.ts +83 -0
- package/lib/schema/format-errors.ts +43 -0
- package/lib/schema/index.ts +4 -0
- package/lib/schema/keywords.ts +96 -0
- package/lib/schema/schema.ts +448 -0
- package/lib/utils.ts +73 -0
- package/package.json +17 -18
- package/scripts/autoinstall-extensions.js +3 -0
- package/build/lib/config-file.d.ts +0 -100
- package/build/lib/config-file.d.ts.map +0 -1
- package/build/lib/config-file.js.map +0 -1
- package/build/lib/config.d.ts +0 -70
- package/build/lib/config.d.ts.map +0 -1
- package/build/lib/config.js +0 -390
- package/build/lib/config.js.map +0 -1
- package/build/lib/grid-register.d.ts +0 -10
- package/build/lib/grid-register.d.ts.map +0 -1
- package/build/lib/grid-register.js +0 -134
- package/build/lib/grid-register.js.map +0 -1
- 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/config.js +0 -389
- package/lib/extension/driver-config.js +0 -245
- package/lib/extension/index.js +0 -169
- package/lib/extension/manifest-migrations.js +0 -136
- package/lib/extension/manifest.js +0 -550
- package/lib/extension/plugin-config.js +0 -112
- package/lib/grid-register.js +0 -146
- package/lib/main.js +0 -545
- 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/index.js +0 -2
- package/lib/schema/keywords.js +0 -136
- package/lib/schema/schema.js +0 -725
- package/lib/utils.js +0 -512
|
@@ -58,50 +58,24 @@ class NotUpdatableError extends Error {
|
|
|
58
58
|
}
|
|
59
59
|
class NoUpdatesAvailableError extends Error {
|
|
60
60
|
}
|
|
61
|
-
/**
|
|
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
|
-
*/
|
|
67
|
-
function receiptToManifest(receipt) {
|
|
68
|
-
return /** @type {ExtManifest<ExtType>} */ (lodash_1.default.omit(receipt, 'driverName', 'pluginName'));
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Fetches the remote extension version requirements
|
|
72
|
-
*
|
|
73
|
-
* @param {string} pkgName Extension name
|
|
74
|
-
* @param {string} [pkgVer] Extension version (if not provided then the latest is assumed)
|
|
75
|
-
* @returns {Promise<[string, string|null]>}
|
|
76
|
-
*/
|
|
77
|
-
async function getRemoteExtensionVersionReq(pkgName, pkgVer) {
|
|
78
|
-
const allDeps = await support_1.npm.getPackageInfo(`${pkgName}${pkgVer ? `@${pkgVer}` : ``}`, ['peerDependencies', 'dependencies']);
|
|
79
|
-
const requiredVersionPair = lodash_1.default.flatMap(lodash_1.default.values(allDeps).map(lodash_1.default.toPairs))
|
|
80
|
-
.find(([name]) => name === 'appium');
|
|
81
|
-
return [utils_2.npmPackage.version, requiredVersionPair ? requiredVersionPair[1] : null];
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* @template {ExtensionType} ExtType
|
|
85
|
-
*/
|
|
86
61
|
class ExtensionCliCommand {
|
|
87
62
|
/**
|
|
88
63
|
* This is the `DriverConfig` or `PluginConfig`, depending on `ExtType`.
|
|
89
|
-
* @type {ExtensionConfig<ExtType>}
|
|
90
64
|
*/
|
|
91
65
|
config;
|
|
92
66
|
/**
|
|
93
67
|
* {@linkcode Record} of official plugins or drivers.
|
|
94
|
-
* @type {KnownExtensions<ExtType>}
|
|
95
68
|
*/
|
|
96
69
|
knownExtensions;
|
|
97
70
|
/**
|
|
98
71
|
* If `true`, command output has been requested as JSON.
|
|
99
|
-
* @type {boolean}
|
|
100
72
|
*/
|
|
101
73
|
isJsonOutput;
|
|
74
|
+
log;
|
|
102
75
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
76
|
+
* Creates an extension command instance.
|
|
77
|
+
*
|
|
78
|
+
* @param opts - constructor options containing extension config and JSON mode
|
|
105
79
|
*/
|
|
106
80
|
constructor({ config, json }) {
|
|
107
81
|
this.config = config;
|
|
@@ -115,24 +89,10 @@ class ExtensionCliCommand {
|
|
|
115
89
|
return this.config.extensionType;
|
|
116
90
|
}
|
|
117
91
|
/**
|
|
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
|
|
92
|
+
* Executes an extension subcommand from parsed CLI args.
|
|
133
93
|
*
|
|
134
|
-
* @param
|
|
135
|
-
* @
|
|
94
|
+
* @param args - parsed CLI argument object
|
|
95
|
+
* @returns result of the executed extension subcommand
|
|
136
96
|
*/
|
|
137
97
|
async execute(args) {
|
|
138
98
|
const cmd = args[`${this.type}Command`];
|
|
@@ -143,11 +103,10 @@ class ExtensionCliCommand {
|
|
|
143
103
|
return await executeCmd(args);
|
|
144
104
|
}
|
|
145
105
|
/**
|
|
146
|
-
*
|
|
106
|
+
* Lists available/installed extensions and optional update metadata.
|
|
147
107
|
*
|
|
148
|
-
* @
|
|
149
|
-
* @
|
|
150
|
-
* @return {Promise<ExtensionList<ExtType>>} map of extension names to extension data
|
|
108
|
+
* @param opts - list command options
|
|
109
|
+
* @returns map of extension names to list data
|
|
151
110
|
*/
|
|
152
111
|
async list({ showInstalled, showUpdates, verbose = false }) {
|
|
153
112
|
const listData = this._buildListData(showInstalled);
|
|
@@ -166,284 +125,33 @@ class ExtensionCliCommand {
|
|
|
166
125
|
return await this._displayNormalListOutput(listData, showUpdates);
|
|
167
126
|
}
|
|
168
127
|
/**
|
|
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
|
|
128
|
+
* Logs a message and returns an {@linkcode Error} to throw.
|
|
381
129
|
*
|
|
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
|
|
130
|
+
* For TS to understand that a function throws an exception, it must actually throw an exception--
|
|
131
|
+
* in other words, _calling_ a function which is guaranteed to throw an exception is not enough--
|
|
132
|
+
* nor is something like a `never` return annotation, which does not imply a thrown exception.
|
|
406
133
|
*
|
|
407
|
-
* @
|
|
408
|
-
* @returns {Promise<void>}
|
|
134
|
+
* @throws {Error}
|
|
409
135
|
*/
|
|
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
|
-
});
|
|
136
|
+
_createFatalError(message) {
|
|
137
|
+
return new Error(this.log.decorate(message, 'error'));
|
|
422
138
|
}
|
|
423
139
|
/**
|
|
424
|
-
*
|
|
140
|
+
* Build the initial list data structure from installed and known extensions
|
|
425
141
|
*
|
|
426
|
-
* @param {InstallOpts} opts
|
|
427
|
-
* @return {Promise<ExtRecord<ExtType>>} map of all installed extension names to extension data
|
|
428
142
|
*/
|
|
429
143
|
async _install({ installSpec, installType, packageName }) {
|
|
430
|
-
/** @type {ExtInstallReceipt<ExtType>} */
|
|
431
|
-
let receipt;
|
|
432
144
|
if (packageName && [extension_config_1.INSTALL_TYPE_LOCAL, extension_config_1.INSTALL_TYPE_NPM].includes(installType)) {
|
|
433
145
|
throw this._createFatalError(`When using --source=${installType}, cannot also use --package`);
|
|
434
146
|
}
|
|
435
147
|
if (!packageName && [extension_config_1.INSTALL_TYPE_GIT, extension_config_1.INSTALL_TYPE_GITHUB].includes(installType)) {
|
|
436
148
|
throw this._createFatalError(`When using --source=${installType}, must also use --package`);
|
|
437
149
|
}
|
|
438
|
-
/**
|
|
439
|
-
* @type {InstallViaNpmArgs}
|
|
440
|
-
*/
|
|
441
150
|
let installViaNpmOpts;
|
|
442
151
|
/**
|
|
443
152
|
* The probable (?) name of the extension derived from the install spec.
|
|
444
153
|
*
|
|
445
154
|
* If using a local install type, this will remain empty.
|
|
446
|
-
* @type {string}
|
|
447
155
|
*/
|
|
448
156
|
let probableExtName = '';
|
|
449
157
|
// depending on `installType`, build the options to pass into `installViaNpm`
|
|
@@ -455,9 +163,9 @@ class ExtensionCliCommand {
|
|
|
455
163
|
installViaNpmOpts = {
|
|
456
164
|
installSpec,
|
|
457
165
|
installType,
|
|
458
|
-
pkgName:
|
|
166
|
+
pkgName: packageName,
|
|
459
167
|
};
|
|
460
|
-
probableExtName =
|
|
168
|
+
probableExtName = packageName;
|
|
461
169
|
}
|
|
462
170
|
else if (installType === extension_config_1.INSTALL_TYPE_GIT) {
|
|
463
171
|
// git urls can have '.git' at the end, but this is not necessary and would complicate the
|
|
@@ -466,12 +174,13 @@ class ExtensionCliCommand {
|
|
|
466
174
|
installViaNpmOpts = {
|
|
467
175
|
installSpec,
|
|
468
176
|
installType,
|
|
469
|
-
pkgName:
|
|
177
|
+
pkgName: packageName,
|
|
470
178
|
};
|
|
471
|
-
probableExtName =
|
|
179
|
+
probableExtName = packageName;
|
|
472
180
|
}
|
|
473
181
|
else {
|
|
474
|
-
let pkgName
|
|
182
|
+
let pkgName;
|
|
183
|
+
let pkgVer;
|
|
475
184
|
if (installType === extension_config_1.INSTALL_TYPE_LOCAL) {
|
|
476
185
|
pkgName = node_path_1.default.isAbsolute(installSpec) ? installSpec : node_path_1.default.resolve(installSpec);
|
|
477
186
|
}
|
|
@@ -522,11 +231,10 @@ class ExtensionCliCommand {
|
|
|
522
231
|
`installed ${this.type}s with "appium ${this.type} list --installed".`);
|
|
523
232
|
}
|
|
524
233
|
await this._checkInstallCompatibility(installViaNpmOpts);
|
|
525
|
-
receipt = await this.installViaNpm(installViaNpmOpts);
|
|
234
|
+
const receipt = await this.installViaNpm(installViaNpmOpts);
|
|
526
235
|
// this _should_ be the same as `probablyExtName` as the one derived above unless
|
|
527
236
|
// install type is local.
|
|
528
|
-
|
|
529
|
-
const extName = receipt[ /** @type {string} */(`${this.type}Name`)];
|
|
237
|
+
const extName = receipt[`${this.type}Name`];
|
|
530
238
|
// check _a second time_ with the more-accurate extName
|
|
531
239
|
if (this.config.isInstalled(extName)) {
|
|
532
240
|
throw this._createFatalError(`A ${this.type} named "${extName}" is already installed. ` +
|
|
@@ -535,7 +243,6 @@ class ExtensionCliCommand {
|
|
|
535
243
|
}
|
|
536
244
|
// this field does not exist as such in the manifest (it's used as a property name instead)
|
|
537
245
|
// so that's why it's being removed here.
|
|
538
|
-
/** @type {ExtManifest<ExtType>} */
|
|
539
246
|
const extManifest = receiptToManifest(receipt);
|
|
540
247
|
const [errors, warnings] = await bluebird_1.default.all([
|
|
541
248
|
this.config.getProblems(extName, extManifest),
|
|
@@ -561,127 +268,17 @@ class ExtensionCliCommand {
|
|
|
561
268
|
return this.config.installedExtensions;
|
|
562
269
|
}
|
|
563
270
|
/**
|
|
564
|
-
*
|
|
271
|
+
* Uninstall an extension.
|
|
272
|
+
*
|
|
273
|
+
* First tries to do this via `npm uninstall`, but if that fails, just `rm -rf`'s the extension dir.
|
|
274
|
+
*
|
|
275
|
+
* Will only remove the extension from the manifest if it has been successfully removed.
|
|
565
276
|
*
|
|
566
|
-
* @
|
|
567
|
-
* @returns {Promise<ExtInstallReceipt<ExtType>>}
|
|
277
|
+
* @return map of all installed extension names to extension data (without the extension just uninstalled)
|
|
568
278
|
*/
|
|
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
|
-
});
|
|
587
|
-
}
|
|
588
|
-
catch (err) {
|
|
589
|
-
throw this._createFatalError(`Encountered an error when installing package: ${err.message}`);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
/**
|
|
593
|
-
* Get the text which should be displayed to the user after an extension has been installed. This
|
|
594
|
-
* is designed to be overridden by drivers/plugins with their own particular text.
|
|
595
|
-
*
|
|
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`);
|
|
279
|
+
async _uninstall({ installSpec }) {
|
|
280
|
+
if (!this.config.isInstalled(installSpec)) {
|
|
281
|
+
throw this._createFatalError(`Can't uninstall ${this.type} '${installSpec}'; it is not installed`);
|
|
685
282
|
}
|
|
686
283
|
const extRecord = this.config.installedExtensions[installSpec];
|
|
687
284
|
if (extRecord.installType === extension_config_1.INSTALL_TYPE_DEV) {
|
|
@@ -699,8 +296,6 @@ class ExtensionCliCommand {
|
|
|
699
296
|
/**
|
|
700
297
|
* Attempt to update one or more drivers using NPM
|
|
701
298
|
*
|
|
702
|
-
* @param {ExtensionUpdateOpts} updateSpec
|
|
703
|
-
* @return {Promise<ExtensionUpdateResult>}
|
|
704
299
|
*/
|
|
705
300
|
async _update({ installSpec, unsafe }) {
|
|
706
301
|
const shouldUpdateAll = installSpec === UPDATE_ALL;
|
|
@@ -712,11 +307,9 @@ class ExtensionCliCommand {
|
|
|
712
307
|
? Object.keys(this.config.installedExtensions)
|
|
713
308
|
: [installSpec];
|
|
714
309
|
// 'errors' will have ext names as keys and error objects as values
|
|
715
|
-
/** @type {Record<string,Error>} */
|
|
716
310
|
const errors = {};
|
|
717
311
|
// 'updates' will have ext names as keys and update objects as values, where an update
|
|
718
312
|
// object is of the form {from: versionString, to: versionString}
|
|
719
|
-
/** @type {Record<string,UpdateReport>} */
|
|
720
313
|
const updates = {};
|
|
721
314
|
for (const e of extsToUpdate) {
|
|
722
315
|
try {
|
|
@@ -738,6 +331,9 @@ class ExtensionCliCommand {
|
|
|
738
331
|
`breaking changes. If you want to apply this update, re-run with --unsafe`);
|
|
739
332
|
}
|
|
740
333
|
const updateVer = unsafe && update.unsafeUpdate ? update.unsafeUpdate : update.safeUpdate;
|
|
334
|
+
if (!updateVer) {
|
|
335
|
+
throw new NoUpdatesAvailableError();
|
|
336
|
+
}
|
|
741
337
|
await (0, utils_1.spinWith)(this.isJsonOutput, `Updating ${this.type} '${e}' from ${update.current} to ${updateVer}`, async () => await this.updateExtension(e, updateVer));
|
|
742
338
|
// if we're doing a safe update, but an unsafe update is also available, let the user know
|
|
743
339
|
if (!unsafe && update.unsafeUpdate) {
|
|
@@ -774,15 +370,13 @@ class ExtensionCliCommand {
|
|
|
774
370
|
* Given an extension name, figure out what its highest possible version upgrade is, and also the
|
|
775
371
|
* highest possible safe upgrade.
|
|
776
372
|
*
|
|
777
|
-
* @param
|
|
778
|
-
* @return {Promise<PossibleUpdates>}
|
|
373
|
+
* @param ext - name of extension
|
|
779
374
|
*/
|
|
780
375
|
async checkForExtensionUpdate(ext) {
|
|
781
376
|
// TODO decide how we want to handle beta versions?
|
|
782
377
|
// this is a helper method, 'ext' is assumed to already be installed here, and of the npm
|
|
783
378
|
// install type
|
|
784
379
|
const { version, pkgName } = this.config.installedExtensions[ext];
|
|
785
|
-
/** @type {string?} */
|
|
786
380
|
let unsafeUpdate = await support_1.npm.getLatestVersion(this.config.appiumHome, pkgName);
|
|
787
381
|
let safeUpdate = await support_1.npm.getLatestSafeUpgradeVersion(this.config.appiumHome, pkgName, version);
|
|
788
382
|
if (unsafeUpdate !== null && !support_1.util.compareVersions(unsafeUpdate, '>', version)) {
|
|
@@ -800,46 +394,10 @@ class ExtensionCliCommand {
|
|
|
800
394
|
}
|
|
801
395
|
return { current: version, safeUpdate, unsafeUpdate };
|
|
802
396
|
}
|
|
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
397
|
/**
|
|
839
398
|
* Runs doctor checks for the given extension.
|
|
840
399
|
*
|
|
841
|
-
* @
|
|
842
|
-
* @returns {Promise<number>} The amount of Doctor checks that were
|
|
400
|
+
* @returns The amount of Doctor checks that were
|
|
843
401
|
* successfully loaded and executed for the given extension
|
|
844
402
|
* @throws {Error} If any of the mandatory Doctor checks fails.
|
|
845
403
|
*/
|
|
@@ -867,7 +425,8 @@ class ExtensionCliCommand {
|
|
|
867
425
|
throw this._createFatalError(`The 'doctor' entry in the package manifest '${packageJsonPath}' must be a proper object ` +
|
|
868
426
|
`containing the 'checks' key with the array of script paths`);
|
|
869
427
|
}
|
|
870
|
-
const paths = doctorSpec.checks
|
|
428
|
+
const paths = doctorSpec.checks
|
|
429
|
+
.map((p) => {
|
|
871
430
|
const scriptPath = node_path_1.default.resolve(moduleRoot, p);
|
|
872
431
|
if (!node_path_1.default.normalize(scriptPath).startsWith(node_path_1.default.normalize(moduleRoot))) {
|
|
873
432
|
this.log.error(`The doctor check script '${p}' from the package manifest '${packageJsonPath}' must be located ` +
|
|
@@ -875,8 +434,8 @@ class ExtensionCliCommand {
|
|
|
875
434
|
return null;
|
|
876
435
|
}
|
|
877
436
|
return scriptPath;
|
|
878
|
-
})
|
|
879
|
-
|
|
437
|
+
})
|
|
438
|
+
.filter((p) => Boolean(p));
|
|
880
439
|
const loadChecksPromises = [];
|
|
881
440
|
for (const p of paths) {
|
|
882
441
|
const promise = (async () => {
|
|
@@ -891,8 +450,7 @@ class ExtensionCliCommand {
|
|
|
891
450
|
})();
|
|
892
451
|
loadChecksPromises.push(promise);
|
|
893
452
|
}
|
|
894
|
-
const isDoctorCheck = (
|
|
895
|
-
/** @type {import('@appium/types').IDoctorCheck[]} */
|
|
453
|
+
const isDoctorCheck = (x) => ['diagnose', 'fix', 'hasAutofix', 'isOptional'].every((method) => lodash_1.default.isFunction(x?.[method]));
|
|
896
454
|
const checks = lodash_1.default.flatMap((await bluebird_1.default.all(loadChecksPromises)).filter(Boolean).map(lodash_1.default.toPairs))
|
|
897
455
|
.map(([, value]) => value)
|
|
898
456
|
.filter(isDoctorCheck);
|
|
@@ -916,10 +474,8 @@ class ExtensionCliCommand {
|
|
|
916
474
|
* `scripts` field is not a plain object, or if the `scriptName` is
|
|
917
475
|
* not found within `scripts` object.
|
|
918
476
|
*
|
|
919
|
-
* @param {RunOptions} opts
|
|
920
|
-
* @return {Promise<RunOutput>}
|
|
921
477
|
*/
|
|
922
|
-
async _run({ installSpec, scriptName, extraArgs = [], bufferOutput = false }) {
|
|
478
|
+
async _run({ installSpec, scriptName, extraArgs = [], bufferOutput = false, }) {
|
|
923
479
|
if (!this.config.isInstalled(installSpec)) {
|
|
924
480
|
throw this._createFatalError(`The ${this.type} "${installSpec}" is not installed`);
|
|
925
481
|
}
|
|
@@ -948,7 +504,7 @@ class ExtensionCliCommand {
|
|
|
948
504
|
this.log.ok(`Successfully retrieved the list of scripts`.green);
|
|
949
505
|
return {};
|
|
950
506
|
}
|
|
951
|
-
if (!(scriptName in
|
|
507
|
+
if (!(scriptName in extScripts)) {
|
|
952
508
|
throw this._createFatalError(`The ${this.type} named '${installSpec}' does not support the script: '${scriptName}'`);
|
|
953
509
|
}
|
|
954
510
|
const scriptPath = extScripts[scriptName];
|
|
@@ -1003,21 +559,335 @@ class ExtensionCliCommand {
|
|
|
1003
559
|
throw this._createFatalError(message);
|
|
1004
560
|
}
|
|
1005
561
|
}
|
|
562
|
+
_buildListData(showInstalled) {
|
|
563
|
+
const installedNames = Object.keys(this.config.installedExtensions);
|
|
564
|
+
const knownNames = Object.keys(this.knownExtensions);
|
|
565
|
+
return [...installedNames, ...knownNames].reduce((acc, name) => {
|
|
566
|
+
if (!acc[name]) {
|
|
567
|
+
if (installedNames.includes(name)) {
|
|
568
|
+
acc[name] = {
|
|
569
|
+
...this.config.installedExtensions[name],
|
|
570
|
+
installed: true,
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
else if (!showInstalled) {
|
|
574
|
+
acc[name] = {
|
|
575
|
+
pkgName: this.knownExtensions[name],
|
|
576
|
+
installed: false,
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return acc;
|
|
581
|
+
}, {});
|
|
582
|
+
}
|
|
583
|
+
/**
|
|
584
|
+
* Install an extension via NPM
|
|
585
|
+
*
|
|
586
|
+
*/
|
|
587
|
+
async installViaNpm({ installSpec, pkgName, pkgVer, installType, }) {
|
|
588
|
+
const installMsg = `Installing '${installSpec}'`;
|
|
589
|
+
const validateMsg = `Validating '${installSpec}'`;
|
|
590
|
+
// the string used for installation is either <name>@<ver> in the case of a standard NPM
|
|
591
|
+
// package, or whatever the user sent in otherwise.
|
|
592
|
+
const installStr = installType === extension_config_1.INSTALL_TYPE_NPM ? `${pkgName}${pkgVer ? `@${pkgVer}` : ''}` : installSpec;
|
|
593
|
+
const appiumHome = this.config.appiumHome;
|
|
594
|
+
try {
|
|
595
|
+
const { pkg, installPath } = await (0, utils_1.spinWith)(this.isJsonOutput, installMsg, async () => await support_1.npm.installPackage(appiumHome, installStr, { pkgName, installType }));
|
|
596
|
+
const validatedPkg = await (0, utils_1.spinWith)(this.isJsonOutput, validateMsg, async () => this.validatePackageJson(pkg, installSpec));
|
|
597
|
+
return this.getInstallationReceipt({
|
|
598
|
+
pkg: validatedPkg,
|
|
599
|
+
installPath,
|
|
600
|
+
installType,
|
|
601
|
+
installSpec,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
604
|
+
catch (err) {
|
|
605
|
+
throw this._createFatalError(`Encountered an error when installing package: ${err.message}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Actually update an extension installed by NPM, using the NPM cli. And update the installation
|
|
610
|
+
* manifest.
|
|
611
|
+
*
|
|
612
|
+
* @param installSpec - name of extension to update
|
|
613
|
+
* @param version - version string identifier to update extension to
|
|
614
|
+
*/
|
|
615
|
+
async updateExtension(installSpec, version) {
|
|
616
|
+
const { pkgName, installType } = this.config.installedExtensions[installSpec];
|
|
617
|
+
const extData = await this.installViaNpm({
|
|
618
|
+
installSpec,
|
|
619
|
+
installType,
|
|
620
|
+
pkgName,
|
|
621
|
+
pkgVer: version,
|
|
622
|
+
});
|
|
623
|
+
delete extData[`${this.type}Name`];
|
|
624
|
+
await this.config.updateExtension(installSpec, extData);
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Just wraps {@linkcode child_process.spawn} with some default options
|
|
628
|
+
*
|
|
629
|
+
* @param cwd - CWD
|
|
630
|
+
* @param script - Path to script
|
|
631
|
+
* @param args - Extra args for script
|
|
632
|
+
* @param opts - Options
|
|
633
|
+
*/
|
|
634
|
+
_runUnbuffered(cwd, script, args = [], opts = {}) {
|
|
635
|
+
return (0, node_child_process_1.spawn)(process.execPath, [script, ...args], {
|
|
636
|
+
cwd,
|
|
637
|
+
stdio: 'inherit',
|
|
638
|
+
...opts,
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Once a package is installed on-disk, this gathers some necessary metadata for validation.
|
|
643
|
+
*
|
|
644
|
+
*/
|
|
645
|
+
getInstallationReceipt({ pkg, installPath, installType, installSpec, }) {
|
|
646
|
+
const { appium, name, version, peerDependencies } = pkg;
|
|
647
|
+
const strVersion = version;
|
|
648
|
+
const internal = {
|
|
649
|
+
pkgName: name,
|
|
650
|
+
version: strVersion,
|
|
651
|
+
installType,
|
|
652
|
+
installSpec,
|
|
653
|
+
installPath,
|
|
654
|
+
appiumVersion: peerDependencies?.appium,
|
|
655
|
+
};
|
|
656
|
+
const extMetadata = appium;
|
|
657
|
+
return {
|
|
658
|
+
...internal,
|
|
659
|
+
...extMetadata,
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
/**
|
|
663
|
+
* Validates the _required_ root fields of an extension's `package.json` file.
|
|
664
|
+
*
|
|
665
|
+
* These required fields are:
|
|
666
|
+
* - `name`
|
|
667
|
+
* - `version`
|
|
668
|
+
* - `appium`
|
|
669
|
+
* @param pkg - `package.json` of extension
|
|
670
|
+
* @param installSpec - Extension name/spec
|
|
671
|
+
* @throws {ReferenceError} If `package.json` has a missing or invalid field
|
|
672
|
+
*/
|
|
673
|
+
validatePackageJson(pkg, installSpec) {
|
|
674
|
+
const { appium, name, version } = pkg;
|
|
675
|
+
const createMissingFieldError = (field) => new ReferenceError(`${this.type} "${installSpec}" invalid; missing a \`${field}\` field of its \`package.json\``);
|
|
676
|
+
if (!name) {
|
|
677
|
+
throw createMissingFieldError('name');
|
|
678
|
+
}
|
|
679
|
+
if (!version) {
|
|
680
|
+
throw createMissingFieldError('version');
|
|
681
|
+
}
|
|
682
|
+
if (!appium) {
|
|
683
|
+
throw createMissingFieldError('appium');
|
|
684
|
+
}
|
|
685
|
+
this.validateExtensionFields(appium, installSpec);
|
|
686
|
+
return pkg;
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Check for available updates for installed extensions
|
|
690
|
+
*
|
|
691
|
+
*/
|
|
692
|
+
async _checkForUpdates(listData, showUpdates, lsMsg) {
|
|
693
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, lsMsg, async () => {
|
|
694
|
+
// We'd like to still show lsMsg even if showUpdates is false
|
|
695
|
+
if (!showUpdates) {
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
// Filter to only extensions that need update checks (installed npm packages)
|
|
699
|
+
const extensionsToCheck = lodash_1.default.toPairs(listData).filter(([, data]) => data.installed && data.installType === extension_config_1.INSTALL_TYPE_NPM);
|
|
700
|
+
await bluebird_1.default.map(extensionsToCheck, async ([ext, data]) => {
|
|
701
|
+
try {
|
|
702
|
+
const updates = await this.checkForExtensionUpdate(ext);
|
|
703
|
+
data.updateVersion = updates.safeUpdate;
|
|
704
|
+
data.unsafeUpdateVersion = updates.unsafeUpdate;
|
|
705
|
+
data.upToDate = updates.safeUpdate === null && updates.unsafeUpdate === null;
|
|
706
|
+
}
|
|
707
|
+
catch (e) {
|
|
708
|
+
data.updateError = e.message;
|
|
709
|
+
}
|
|
710
|
+
}, { concurrency: MAX_CONCURRENT_REPO_FETCHES });
|
|
711
|
+
});
|
|
712
|
+
}
|
|
713
|
+
/**
|
|
714
|
+
* Add repository URLs to list data for all extensions
|
|
715
|
+
*
|
|
716
|
+
*/
|
|
717
|
+
async _addRepositoryUrlsToListData(listData) {
|
|
718
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, 'Fetching repository information', async () => {
|
|
719
|
+
await bluebird_1.default.map(lodash_1.default.values(listData), async (data) => {
|
|
720
|
+
const repoUrl = await this._getRepositoryUrl(data);
|
|
721
|
+
if (repoUrl) {
|
|
722
|
+
data.repositoryUrl = repoUrl;
|
|
723
|
+
}
|
|
724
|
+
}, { concurrency: MAX_CONCURRENT_REPO_FETCHES });
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Display normal formatted output
|
|
729
|
+
*
|
|
730
|
+
*/
|
|
731
|
+
async _displayNormalListOutput(listData, showUpdates) {
|
|
732
|
+
for (const [name, data] of lodash_1.default.toPairs(listData)) {
|
|
733
|
+
const line = await this._formatExtensionLine(name, data, showUpdates);
|
|
734
|
+
this.log.log(line);
|
|
735
|
+
}
|
|
736
|
+
return listData;
|
|
737
|
+
}
|
|
738
|
+
/**
|
|
739
|
+
* Format a single extension line for display
|
|
740
|
+
*
|
|
741
|
+
*/
|
|
742
|
+
async _formatExtensionLine(name, data, showUpdates) {
|
|
743
|
+
if (data.installed) {
|
|
744
|
+
const installTxt = this._formatInstallText(data);
|
|
745
|
+
const updateTxt = showUpdates ? this._formatUpdateText(data) : '';
|
|
746
|
+
return `- ${name.yellow}${installTxt}${updateTxt}`;
|
|
747
|
+
}
|
|
748
|
+
const installTxt = ' [not installed]'.grey;
|
|
749
|
+
return `- ${name.yellow}${installTxt}`;
|
|
750
|
+
}
|
|
751
|
+
/**
|
|
752
|
+
* Format installation status text
|
|
753
|
+
*
|
|
754
|
+
*/
|
|
755
|
+
_formatInstallText(data) {
|
|
756
|
+
const { installType, installSpec, version } = data;
|
|
757
|
+
let typeTxt;
|
|
758
|
+
switch (installType) {
|
|
759
|
+
case extension_config_1.INSTALL_TYPE_GIT:
|
|
760
|
+
case extension_config_1.INSTALL_TYPE_GITHUB:
|
|
761
|
+
typeTxt = `(cloned from ${installSpec})`.yellow;
|
|
762
|
+
break;
|
|
763
|
+
case extension_config_1.INSTALL_TYPE_LOCAL:
|
|
764
|
+
typeTxt = `(linked from ${installSpec})`.magenta;
|
|
765
|
+
break;
|
|
766
|
+
case extension_config_1.INSTALL_TYPE_DEV:
|
|
767
|
+
typeTxt = '(dev mode)';
|
|
768
|
+
break;
|
|
769
|
+
default:
|
|
770
|
+
typeTxt = '(npm)';
|
|
771
|
+
}
|
|
772
|
+
return `@${String(version).yellow} ${('[installed ' + typeTxt + ']').green}`;
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Format update information text
|
|
776
|
+
*
|
|
777
|
+
*/
|
|
778
|
+
_formatUpdateText(data) {
|
|
779
|
+
const { updateVersion, unsafeUpdateVersion, upToDate, updateError } = data;
|
|
780
|
+
if (updateError) {
|
|
781
|
+
return ` [Cannot check for updates: ${updateError}]`.red;
|
|
782
|
+
}
|
|
783
|
+
let txt = '';
|
|
784
|
+
if (updateVersion) {
|
|
785
|
+
txt += ` [${updateVersion} available]`.magenta;
|
|
786
|
+
}
|
|
787
|
+
if (upToDate) {
|
|
788
|
+
txt += ` [Up to date]`.green;
|
|
789
|
+
}
|
|
790
|
+
if (unsafeUpdateVersion) {
|
|
791
|
+
txt += ` [${unsafeUpdateVersion} available (potentially unsafe)]`.cyan;
|
|
792
|
+
}
|
|
793
|
+
return txt;
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Get repository URL from package data
|
|
797
|
+
*
|
|
798
|
+
*/
|
|
799
|
+
async _getRepositoryUrl(data) {
|
|
800
|
+
if (data.installed && data.installPath) {
|
|
801
|
+
return await this._getRepositoryUrlFromInstalled(data);
|
|
802
|
+
}
|
|
803
|
+
if (data.pkgName && !data.installed) {
|
|
804
|
+
return await this._getRepositoryUrlFromNpm(data.pkgName);
|
|
805
|
+
}
|
|
806
|
+
return null;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Get repository URL from installed extension's package.json
|
|
810
|
+
*
|
|
811
|
+
*/
|
|
812
|
+
async _getRepositoryUrlFromInstalled(data) {
|
|
813
|
+
try {
|
|
814
|
+
const pkgJsonPath = node_path_1.default.join(String(data.installPath), 'package.json');
|
|
815
|
+
if (await support_1.fs.exists(pkgJsonPath)) {
|
|
816
|
+
const pkg = JSON.parse(await support_1.fs.readFile(pkgJsonPath, 'utf8'));
|
|
817
|
+
if (pkg.repository) {
|
|
818
|
+
if (typeof pkg.repository === 'string') {
|
|
819
|
+
return pkg.repository;
|
|
820
|
+
}
|
|
821
|
+
if (pkg.repository.url) {
|
|
822
|
+
return pkg.repository.url.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
catch {
|
|
828
|
+
// Ignore errors reading package.json
|
|
829
|
+
}
|
|
830
|
+
return null;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* Get repository URL from npm for a package name
|
|
834
|
+
*
|
|
835
|
+
*/
|
|
836
|
+
async _getRepositoryUrlFromNpm(pkgName) {
|
|
837
|
+
try {
|
|
838
|
+
const repoInfo = await support_1.npm.getPackageInfo(pkgName, ['repository']);
|
|
839
|
+
// When requesting only 'repository', npm.getPackageInfo returns the repository object directly
|
|
840
|
+
if (repoInfo) {
|
|
841
|
+
if (typeof repoInfo === 'string') {
|
|
842
|
+
return repoInfo;
|
|
843
|
+
}
|
|
844
|
+
if (repoInfo.url) {
|
|
845
|
+
return repoInfo.url.replace(/^git\+/, '').replace(/\.git$/, '');
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
catch {
|
|
850
|
+
// Ignore errors fetching from npm
|
|
851
|
+
}
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
/**
|
|
855
|
+
* Checks whether the given extension is compatible with the currently installed server
|
|
856
|
+
*
|
|
857
|
+
*/
|
|
858
|
+
async _checkInstallCompatibility({ installSpec, pkgName, pkgVer, installType, }) {
|
|
859
|
+
if (extension_config_1.INSTALL_TYPE_NPM !== installType) {
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
await (0, utils_1.spinWith)(this.isJsonOutput, `Checking if '${pkgName}' is compatible`, async () => {
|
|
863
|
+
const [serverVersion, extVersionRequirement] = await getRemoteExtensionVersionReq(pkgName, pkgVer);
|
|
864
|
+
if (serverVersion && extVersionRequirement && !semver.satisfies(serverVersion, extVersionRequirement)) {
|
|
865
|
+
throw this._createFatalError(`'${installSpec}' cannot be installed because the server version it requires (${extVersionRequirement}) ` +
|
|
866
|
+
`does not meet the currently installed one (${serverVersion}). Please install ` +
|
|
867
|
+
`a compatible server version first.`);
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
}
|
|
1006
871
|
}
|
|
1007
872
|
exports.ExtensionCommand = ExtensionCliCommand;
|
|
1008
873
|
/**
|
|
1009
874
|
* This is needed to ensure proper module resolution for installed extensions,
|
|
1010
875
|
* especially ESM ones.
|
|
1011
876
|
*
|
|
1012
|
-
* @param
|
|
1013
|
-
* @param
|
|
1014
|
-
* @param
|
|
877
|
+
* @param driverConfig - active driver extension config
|
|
878
|
+
* @param pluginConfig - active plugin extension config
|
|
879
|
+
* @param logger - logger instance used for non-fatal symlink errors
|
|
880
|
+
* @returns resolves when symlink injection has completed for all extensions
|
|
1015
881
|
*/
|
|
1016
882
|
async function injectAppiumSymlinks(driverConfig, pluginConfig, logger) {
|
|
1017
|
-
const
|
|
883
|
+
const isNpmInstalledExtension = (details) => details.installType === extension_config_1.INSTALL_TYPE_NPM && Boolean(details.installPath);
|
|
884
|
+
const installedExtensions = [
|
|
1018
885
|
...Object.values(driverConfig.installedExtensions || {}),
|
|
1019
|
-
...Object.values(pluginConfig.installedExtensions || {})
|
|
1020
|
-
]
|
|
886
|
+
...Object.values(pluginConfig.installedExtensions || {}),
|
|
887
|
+
];
|
|
888
|
+
const installPaths = lodash_1.default.compact(installedExtensions
|
|
889
|
+
.filter((details) => Boolean(details))
|
|
890
|
+
.filter(isNpmInstalledExtension)
|
|
1021
891
|
.map((details) => details.installPath));
|
|
1022
892
|
// After the extension is installed, we try to inject the appium module symlink
|
|
1023
893
|
// into the extension's node_modules folder if it is not there yet.
|
|
@@ -1026,16 +896,32 @@ async function injectAppiumSymlinks(driverConfig, pluginConfig, logger) {
|
|
|
1026
896
|
// (see https://github.com/appium/python-client/pull/1177#issuecomment-3419826643).
|
|
1027
897
|
await Promise.all(installPaths.map((installPath) => injectAppiumSymlink(node_path_1.default.join(installPath, 'node_modules'), logger)));
|
|
1028
898
|
}
|
|
899
|
+
/**
|
|
900
|
+
* Omits `driverName`/`pluginName` props from the receipt to make a {@linkcode ExtManifest}
|
|
901
|
+
*/
|
|
902
|
+
function receiptToManifest(receipt) {
|
|
903
|
+
return lodash_1.default.omit(receipt, 'driverName', 'pluginName');
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Fetches the remote extension version requirements
|
|
907
|
+
*
|
|
908
|
+
* @param pkgName Extension name
|
|
909
|
+
* @param [pkgVer] Extension version (if not provided then the latest is assumed)
|
|
910
|
+
*/
|
|
911
|
+
async function getRemoteExtensionVersionReq(pkgName, pkgVer) {
|
|
912
|
+
const allDeps = await support_1.npm.getPackageInfo(`${pkgName}${pkgVer ? `@${pkgVer}` : ``}`, ['peerDependencies', 'dependencies']);
|
|
913
|
+
const requiredVersionPair = lodash_1.default.flatMap(lodash_1.default.values(allDeps).map(lodash_1.default.toPairs))
|
|
914
|
+
.find(([name]) => name === 'appium');
|
|
915
|
+
return [utils_2.npmPackage.version, requiredVersionPair ? requiredVersionPair[1] : null];
|
|
916
|
+
}
|
|
1029
917
|
/**
|
|
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
|