appium 2.2.3 → 2.4.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.
Files changed (90) hide show
  1. package/README.md +131 -158
  2. package/build/lib/appium.d.ts +76 -4
  3. package/build/lib/appium.d.ts.map +1 -1
  4. package/build/lib/appium.js +327 -1
  5. package/build/lib/appium.js.map +1 -1
  6. package/build/lib/cli/args.d.ts.map +1 -1
  7. package/build/lib/cli/args.js +24 -2
  8. package/build/lib/cli/args.js.map +1 -1
  9. package/build/lib/cli/driver-command.d.ts +16 -0
  10. package/build/lib/cli/driver-command.d.ts.map +1 -1
  11. package/build/lib/cli/driver-command.js +16 -0
  12. package/build/lib/cli/driver-command.js.map +1 -1
  13. package/build/lib/cli/extension-command.d.ts +26 -2
  14. package/build/lib/cli/extension-command.d.ts.map +1 -1
  15. package/build/lib/cli/extension-command.js +135 -4
  16. package/build/lib/cli/extension-command.js.map +1 -1
  17. package/build/lib/cli/parser.d.ts.map +1 -1
  18. package/build/lib/cli/parser.js +11 -7
  19. package/build/lib/cli/parser.js.map +1 -1
  20. package/build/lib/cli/plugin-command.d.ts +16 -0
  21. package/build/lib/cli/plugin-command.d.ts.map +1 -1
  22. package/build/lib/cli/plugin-command.js +16 -0
  23. package/build/lib/cli/plugin-command.js.map +1 -1
  24. package/build/lib/cli/utils.d.ts.map +1 -1
  25. package/build/lib/cli/utils.js +3 -4
  26. package/build/lib/cli/utils.js.map +1 -1
  27. package/build/lib/config-file.d.ts +1 -1
  28. package/build/lib/config.d.ts +8 -7
  29. package/build/lib/config.d.ts.map +1 -1
  30. package/build/lib/config.js +54 -64
  31. package/build/lib/config.js.map +1 -1
  32. package/build/lib/constants.d.ts +10 -0
  33. package/build/lib/constants.d.ts.map +1 -1
  34. package/build/lib/constants.js +11 -1
  35. package/build/lib/constants.js.map +1 -1
  36. package/build/lib/doctor/doctor.d.ts +55 -0
  37. package/build/lib/doctor/doctor.d.ts.map +1 -0
  38. package/build/lib/doctor/doctor.js +201 -0
  39. package/build/lib/doctor/doctor.js.map +1 -0
  40. package/build/lib/extension/driver-config.d.ts +2 -2
  41. package/build/lib/extension/driver-config.d.ts.map +1 -1
  42. package/build/lib/extension/driver-config.js +4 -4
  43. package/build/lib/extension/driver-config.js.map +1 -1
  44. package/build/lib/extension/extension-config.d.ts +10 -3
  45. package/build/lib/extension/extension-config.d.ts.map +1 -1
  46. package/build/lib/extension/extension-config.js +32 -14
  47. package/build/lib/extension/extension-config.js.map +1 -1
  48. package/build/lib/extension/index.d.ts +6 -4
  49. package/build/lib/extension/index.d.ts.map +1 -1
  50. package/build/lib/extension/index.js +74 -32
  51. package/build/lib/extension/index.js.map +1 -1
  52. package/build/lib/extension/manifest.js +1 -1
  53. package/build/lib/extension/manifest.js.map +1 -1
  54. package/build/lib/extension/plugin-config.js +1 -1
  55. package/build/lib/extension/plugin-config.js.map +1 -1
  56. package/build/lib/grid-register.d.ts.map +1 -1
  57. package/build/lib/grid-register.js +1 -2
  58. package/build/lib/grid-register.js.map +1 -1
  59. package/build/lib/main.d.ts +4 -0
  60. package/build/lib/main.d.ts.map +1 -1
  61. package/build/lib/main.js +31 -52
  62. package/build/lib/main.js.map +1 -1
  63. package/build/lib/schema/schema.d.ts +5 -0
  64. package/build/lib/schema/schema.d.ts.map +1 -1
  65. package/build/lib/schema/schema.js +7 -7
  66. package/build/lib/schema/schema.js.map +1 -1
  67. package/build/lib/utils.d.ts +3 -0
  68. package/build/lib/utils.d.ts.map +1 -1
  69. package/build/lib/utils.js +2 -1
  70. package/build/lib/utils.js.map +1 -1
  71. package/build/types/cli.d.ts +1 -1
  72. package/build/types/cli.d.ts.map +1 -1
  73. package/lib/appium.js +388 -2
  74. package/lib/cli/args.js +27 -2
  75. package/lib/cli/driver-command.js +18 -0
  76. package/lib/cli/extension-command.js +160 -9
  77. package/lib/cli/parser.js +22 -8
  78. package/lib/cli/plugin-command.js +18 -0
  79. package/lib/cli/utils.js +5 -7
  80. package/lib/config.js +46 -65
  81. package/lib/constants.js +12 -0
  82. package/lib/doctor/doctor.js +209 -0
  83. package/lib/extension/driver-config.js +3 -3
  84. package/lib/extension/extension-config.js +33 -15
  85. package/lib/extension/index.js +80 -43
  86. package/lib/grid-register.js +1 -2
  87. package/lib/main.js +42 -61
  88. package/lib/utils.js +2 -1
  89. package/package.json +15 -18
  90. package/types/cli.ts +1 -1
@@ -0,0 +1,209 @@
1
+ import '@colors/colors';
2
+ import _ from 'lodash';
3
+ import { util, doctor, logger } from '@appium/support';
4
+
5
+ export class Doctor {
6
+ /**
7
+ * @param {DoctorCheck[]} [checks=[]]
8
+ */
9
+ constructor(checks = []) {
10
+ this.log = logger.getLogger('Doctor');
11
+ /** @type {DoctorCheck[]} */
12
+ this.checks = checks;
13
+ this.checks
14
+ .filter((c) => _.isNil(c.log))
15
+ .forEach((c) => { c.log = this.log; });
16
+ /** @type {DoctorIssue[]} */
17
+ this.foundIssues = [];
18
+ }
19
+
20
+ /**
21
+ * @returns {DoctorIssue[]}
22
+ */
23
+ get issuesRequiredToFix() {
24
+ return this.foundIssues.filter((f) => !f.check.isOptional());
25
+ }
26
+
27
+ /**
28
+ * @returns {DoctorIssue[]}
29
+ */
30
+ get issuesOptionalToFix() {
31
+ return this.foundIssues.filter((f) => f.check.isOptional());
32
+ }
33
+
34
+ /**
35
+ * The doctor shows the report
36
+ */
37
+ async diagnose() {
38
+ this.log.info(`### Starting doctor diagnostics ###`);
39
+ this.foundIssues = [];
40
+ for (const check of this.checks) {
41
+ const res = await check.diagnose();
42
+ const issue = this.toIssue(res, check);
43
+ if (issue) {
44
+ this.foundIssues.push(issue);
45
+ }
46
+ }
47
+ this.log.info(
48
+ `### Diagnostic completed, ${this.buildFixMessage()}. ###`
49
+ );
50
+ this.log.info('');
51
+ }
52
+
53
+ /**
54
+ * @returns {Promise<boolean>}
55
+ */
56
+ async reportManualIssues() {
57
+ const manualIssues = _.filter(this.issuesRequiredToFix, (f) => !f.check.hasAutofix());
58
+ const manualIssuesOptional = _.filter(this.issuesOptionalToFix, (f) => !f.check.hasAutofix());
59
+
60
+ const handleIssues = async (headerLogs, issues) => {
61
+ if (_.isEmpty(issues)) {
62
+ return;
63
+ }
64
+
65
+ for (const logMsg of headerLogs) {
66
+ this.log.info(logMsg);
67
+ }
68
+ /** @type {string[]} */
69
+ const fixMessages = [];
70
+ for (const issue of issues) {
71
+ const message = await issue.check.fix();
72
+ if (message) {
73
+ fixMessages.push(message);
74
+ }
75
+ }
76
+ for (const m of _.uniq(fixMessages)) {
77
+ this.log.warn(` \u279C ${m}`);
78
+ }
79
+ this.log.info('');
80
+ };
81
+
82
+ await handleIssues([
83
+ '### Manual Fixes Needed ###',
84
+ 'The configuration cannot be automatically fixed, please do the following first:',
85
+ ], manualIssues);
86
+ await handleIssues([
87
+ '### Optional Manual Fixes ###',
88
+ 'To fix these optional issues, please do the following manually:',
89
+ ], manualIssuesOptional);
90
+
91
+ if (manualIssues.length > 0) {
92
+ this.log.info('###');
93
+ this.log.info('');
94
+ this.log.info('Bye! Run doctor again when all manual fixes have been applied!');
95
+ this.log.info('');
96
+ return true;
97
+ }
98
+ return false;
99
+ }
100
+
101
+ /**
102
+ * @param {DoctorIssue} f
103
+ */
104
+ async runAutoFix(f) {
105
+ this.log.info(`### Fixing: ${f.error} ###`);
106
+ try {
107
+ await f.check.fix();
108
+ } catch (err) {
109
+ if (err.constructor.name === doctor.FixSkippedError.name) {
110
+ this.log.info(`### Skipped fix ###`);
111
+ return;
112
+ } else {
113
+ this.log.warn(`${err}`.replace(/\n$/g, ''));
114
+ this.log.info(`### Fix did not succeed ###`);
115
+ return;
116
+ }
117
+ }
118
+ this.log.info('Checking if this was fixed:');
119
+ const res = await f.check.diagnose();
120
+ if (res.ok) {
121
+ f.fixed = true;
122
+ this.log.info(` ${'\u2714'.green} ${res.message}`);
123
+ this.log.info(`### Fix was successfully applied ###`);
124
+ } else {
125
+ this.log.info(` ${'\u2716'.red} ${res.message}`);
126
+ this.log.info(`### Fix was applied but issue remains ###`);
127
+ }
128
+ }
129
+
130
+ async runAutoFixes() {
131
+ const autoFixes = _.filter(this.foundIssues, (f) => f.check.hasAutofix());
132
+ for (const f of autoFixes) {
133
+ await this.runAutoFix(f);
134
+ this.log.info('');
135
+ }
136
+ if (_.find(autoFixes, (f) => !f.fixed)) {
137
+ // a few issues remain.
138
+ this.log.info('Bye! A few issues remain, fix manually and/or rerun doctor!');
139
+ } else {
140
+ // nothing left to fix.
141
+ this.log.info('Bye! All issues have been fixed!');
142
+ }
143
+ this.log.info('');
144
+ }
145
+
146
+ async run() {
147
+ await this.diagnose();
148
+ if (this.reportSuccess()) {
149
+ return;
150
+ }
151
+ if (await this.reportManualIssues()) {
152
+ return;
153
+ }
154
+ await this.runAutoFixes();
155
+ }
156
+
157
+ /**
158
+ * @param {DoctorCheckResult} result
159
+ * @param {DoctorCheck} check
160
+ * @returns {DoctorIssue?}
161
+ */
162
+ toIssue(result, check) {
163
+ if (result.ok) {
164
+ this.log.info(` ${'\u2714'.green} ${result.message}`);
165
+ return null;
166
+ }
167
+
168
+ const errorMessage = result.optional
169
+ ? ` ${'\u2716'.yellow} ${result.message}`
170
+ : ` ${'\u2716'.red} ${result.message}`;
171
+ this.log.warn(errorMessage);
172
+ return {
173
+ error: errorMessage,
174
+ check,
175
+ };
176
+ }
177
+
178
+ /**
179
+ * @returns {string}
180
+ */
181
+ buildFixMessage() {
182
+ return `${util.pluralize('required fix', this.issuesRequiredToFix.length, true)} needed, ` +
183
+ `${util.pluralize('optional fix', this.issuesOptionalToFix.length, true)} possible`;
184
+ }
185
+
186
+ /**
187
+ * @returns {boolean}
188
+ */
189
+ reportSuccess() {
190
+ if (this.issuesRequiredToFix.length === 0 && this.issuesOptionalToFix.length === 0) {
191
+ this.log.info('Everything looks good, bye!');
192
+ this.log.info('');
193
+ return true;
194
+ }
195
+ return false;
196
+ }
197
+ }
198
+
199
+ /**
200
+ * @typedef DoctorIssue
201
+ * @property {DoctorCheck} check
202
+ * @property {string} error
203
+ * @property {boolean} [fixed]
204
+ */
205
+
206
+ /**
207
+ * @typedef {import('@appium/types').IDoctorCheck} DoctorCheck
208
+ * @typedef {import('@appium/types').DoctorCheckResult} DoctorCheckResult
209
+ */
@@ -135,9 +135,9 @@ export class DriverConfig extends ExtensionConfig {
135
135
  * Given capabilities, find a matching driver within the config. Load its class and return it along with version and driver name.
136
136
  * @template {import('@appium/types').StringRecord} C
137
137
  * @param {C} caps
138
- * @returns {MatchedDriver}
138
+ * @returns {Promise<MatchedDriver>}
139
139
  */
140
- findMatchingDriver({automationName, platformName}) {
140
+ async findMatchingDriver({automationName, platformName}) {
141
141
  if (!_.isString(platformName)) {
142
142
  throw new Error('You must include a platformName capability');
143
143
  }
@@ -158,7 +158,7 @@ export class DriverConfig extends ExtensionConfig {
158
158
  );
159
159
  log.info(`The '${driverName}' driver was installed and matched caps.`);
160
160
  log.info(`Will require it at ${this.getInstallPath(driverName)}`);
161
- const driver = this.require(driverName);
161
+ const driver = await this.requireAsync(driverName);
162
162
  if (!driver) {
163
163
  throw new Error(
164
164
  `Driver '${driverName}' did not export a class with name '${mainClass}'. Contact the author of the driver!`
@@ -1,4 +1,4 @@
1
- import {util} from '@appium/support';
1
+ import {util, fs, system} from '@appium/support';
2
2
  import B from 'bluebird';
3
3
  import _ from 'lodash';
4
4
  import path from 'path';
@@ -12,6 +12,7 @@ import {
12
12
  isAllowedSchemaFileExtension,
13
13
  registerSchema,
14
14
  } from '../schema/schema';
15
+ import { pathToFileURL } from 'url';
15
16
 
16
17
  /**
17
18
  * "npm" install type
@@ -554,28 +555,45 @@ export class ExtensionConfig {
554
555
  }
555
556
 
556
557
  /**
557
- * Loads extension and returns its main class (constructor)
558
+ *
558
559
  * @param {ExtName<ExtType>} extName
559
- * @returns {ExtClass<ExtType>}
560
+ * @returns {Promise<[string, string]>}
560
561
  */
561
- require(extName) {
562
+ async _resolveExtension(extName) {
562
563
  const {mainClass} = this.installedExtensions[extName];
563
- const reqPath = this.getInstallPath(extName);
564
- /** @type {string} */
565
- let reqResolved;
564
+ const moduleRoot = this.getInstallPath(extName);
565
+ const packageJsonPath = path.join(moduleRoot, 'package.json');
566
+ let entryPointPath;
566
567
  try {
567
- reqResolved = require.resolve(reqPath);
568
- } catch (err) {
569
- throw new ReferenceError(`Could not find a ${this.extensionType} installed at ${reqPath}`);
568
+ entryPointPath = JSON.parse(await fs.readFile(packageJsonPath, 'utf8')).main ?? 'index.js';
569
+ } catch (e) {
570
+ throw new ReferenceError(`Could not find a ${this.extensionType} installed at ${moduleRoot}`);
571
+ }
572
+ if (entryPointPath) {
573
+ entryPointPath = path.resolve(moduleRoot, entryPointPath);
574
+ } else {
575
+ throw new ReferenceError(`Cannot find a valid ${this.extensionType} main entry point in '${packageJsonPath}'`);
570
576
  }
571
577
  // note: this will only reload the entry point
572
- if (process.env.APPIUM_RELOAD_EXTENSIONS && require.cache[reqResolved]) {
573
- log.debug(`Removing ${reqResolved} from require cache`);
574
- delete require.cache[reqResolved];
578
+ if (process.env.APPIUM_RELOAD_EXTENSIONS && require.cache[entryPointPath]) {
579
+ log.debug(`Removing ${entryPointPath} from require cache`);
580
+ delete require.cache[entryPointPath];
575
581
  }
582
+ return [entryPointPath, mainClass];
583
+ }
584
+
585
+ /**
586
+ * Loads extension asynchronously and returns its main class (constructor)
587
+ *
588
+ * @param {ExtName<ExtType>} extName
589
+ * @returns {Promise<ExtClass<ExtType>>}
590
+ */
591
+ async requireAsync(extName) {
592
+ const [reqPath, mainClass] = await this._resolveExtension(extName);
576
593
  log.debug(`Requiring ${this.extensionType} at ${reqPath}`);
577
- // eslint-disable-next-line @typescript-eslint/no-var-requires
578
- const MainClass = require(reqPath)[mainClass];
594
+ // https://github.com/nodejs/node/issues/31710
595
+ const importPath = system.isWindows() ? pathToFileURL(reqPath).href : reqPath;
596
+ const MainClass = (await import(importPath))[mainClass];
579
597
  if (!MainClass) {
580
598
  throw new ReferenceError(
581
599
  `Could not find a class named "${mainClass}" exported by ${this.extensionType} "${extName}"`
@@ -3,6 +3,7 @@ import {USE_ALL_PLUGINS} from '../constants';
3
3
  import log from '../logger';
4
4
  import {DriverConfig} from './driver-config';
5
5
  import {Manifest} from './manifest';
6
+ import {timing} from '@appium/support';
6
7
  import {PluginConfig} from './plugin-config';
7
8
  import B from 'bluebird';
8
9
 
@@ -27,6 +28,47 @@ export async function loadExtensions(appiumHome) {
27
28
  return {driverConfig, pluginConfig};
28
29
  }
29
30
 
31
+ /**
32
+ * @template {'driver'|'plugin'} TExtType
33
+ * @param {TExtType} extType
34
+ * @param {import('./extension-config').ExtensionConfig} config
35
+ * @param {string[]} extNames
36
+ * @param {number} asyncImportChunkSize
37
+ * @returns {Promise<[TExtType extends 'driver' ? DriverClass : PluginClass, string][]>}
38
+ */
39
+ async function importExtensions(extType, config, extNames, asyncImportChunkSize) {
40
+ /** @type {B[]} */
41
+ const allPromises = [];
42
+ /** @type {B[]} */
43
+ const activePromisesChunk = [];
44
+ for (const extName of extNames) {
45
+ _.remove(activePromisesChunk, (p) => p.isFulfilled());
46
+ if (activePromisesChunk.length >= asyncImportChunkSize) {
47
+ await B.any(activePromisesChunk);
48
+ }
49
+ const promise = B.resolve((async () => {
50
+ log.info(`Attempting to load ${extType} ${extName}...`);
51
+ const timer = new timing.Timer().start();
52
+ try {
53
+ const extClass = await config.requireAsync(extName);
54
+ log.debug(`${extClass.name} has been successfully loaded in ${timer.getDuration().asSeconds.toFixed(3)}s`);
55
+ return extClass;
56
+ } catch (err) {
57
+ log.error(
58
+ `Could not load ${extType} '${extName}', so it will not be available. Error ` +
59
+ `in loading the ${extType} was: ${err.message}`
60
+ );
61
+ log.debug(err.stack);
62
+ }
63
+ })());
64
+ activePromisesChunk.push(promise);
65
+ allPromises.push(promise);
66
+ }
67
+ return /** @type {[TExtType extends 'driver' ? DriverClass : PluginClass, string][]} */ (
68
+ _.zip(await B.all(allPromises), extNames).filter(([extClass,]) => Boolean(extClass))
69
+ );
70
+ }
71
+
30
72
  /**
31
73
  * Find any plugin name which has been installed, and which has been requested for activation by
32
74
  * using the --use-plugins flag, and turn each one into its class, so we can send them as objects
@@ -34,10 +76,15 @@ export async function loadExtensions(appiumHome) {
34
76
  * to wrap command execution
35
77
  *
36
78
  * @param {import('./plugin-config').PluginConfig} pluginConfig - a plugin extension config
79
+ * @param {number} maxParallelImports the maximum amount of plugins to import in parallel
37
80
  * @param {string[]} usePlugins
38
- * @returns {PluginNameMap} Mapping of PluginClass to name
81
+ * @returns {Promise<PluginNameMap>} Mapping of PluginClass to name
39
82
  */
40
- export function getActivePlugins(pluginConfig, usePlugins = []) {
83
+ export async function getActivePlugins(pluginConfig, maxParallelImports, usePlugins = []) {
84
+ if (_.isEmpty(usePlugins)) {
85
+ return new Map();
86
+ }
87
+
41
88
  /** @type {string[]} */
42
89
  let filteredPluginNames = [];
43
90
  if (usePlugins.length === 1 && usePlugins[0] === USE_ALL_PLUGINS) {
@@ -52,31 +99,17 @@ export function getActivePlugins(pluginConfig, usePlugins = []) {
52
99
  `The reserved plugin name '${pluginName}' cannot be combined with other names.`
53
100
  );
54
101
  } else {
102
+ const suffix = _.isEmpty(pluginConfig.installedExtensions)
103
+ ? `You don't have any plugins installed yet.`
104
+ : `Only the following ${_.size(pluginConfig.installedExtensions) === 1 ? `plugin is` : `plugins are`} ` +
105
+ `available: ${_.keys(pluginConfig.installedExtensions)}`;
55
106
  throw new Error(
56
- `Could not load the plugin '${pluginName}' because it is not installed. ` +
57
- `Only the following plugins are available: ${_.keys(pluginConfig.installedExtensions)}`
107
+ `Could not load the plugin '${pluginName}' because it is not installed. ${suffix}`
58
108
  );
59
109
  }
60
110
  }
61
111
  }
62
- return new Map(
63
- _.compact(
64
- filteredPluginNames
65
- .map((pluginName) => {
66
- try {
67
- log.info(`Attempting to load plugin ${pluginName}...`);
68
- const PluginClass = pluginConfig.require(pluginName);
69
- return [PluginClass, pluginName];
70
- } catch (err) {
71
- log.error(
72
- `Could not load plugin '${pluginName}', so it will not be available. Error ` +
73
- `in loading the plugin was: ${err.message}`
74
- );
75
- log.debug(err.stack);
76
- }
77
- })
78
- )
79
- );
112
+ return new Map(await importExtensions('plugin', pluginConfig, filteredPluginNames, maxParallelImports));
80
113
  }
81
114
 
82
115
  /**
@@ -85,29 +118,33 @@ export function getActivePlugins(pluginConfig, usePlugins = []) {
85
118
  * If the --drivers flag was given, this method only loads the given drivers.
86
119
  *
87
120
  * @param {import('./driver-config').DriverConfig} driverConfig - a driver extension config
121
+ * @param {number} maxParallelImports the maximum amount of plugins to import in parallel
88
122
  * @param {string[]} [useDrivers] - optional list of drivers to load
89
- * @returns {DriverNameMap}
123
+ * @returns {Promise<DriverNameMap>}
90
124
  */
91
- export function getActiveDrivers(driverConfig, useDrivers = []) {
92
- return new Map(
93
- _.compact(
94
- Object.keys(driverConfig.installedExtensions)
95
- .filter((driverName) => _.includes(useDrivers, driverName) || useDrivers.length === 0)
96
- .map((driverName) => {
97
- try {
98
- log.info(`Attempting to load driver ${driverName}...`);
99
- const DriverClass = driverConfig.require(driverName);
100
- return [DriverClass, driverName];
101
- } catch (err) {
102
- log.error(
103
- `Could not load driver '${driverName}', so it will not be available. Error ` +
104
- `in loading the driver was: ${err.message}`
105
- );
106
- log.debug(err.stack);
107
- }
108
- })
109
- )
110
- );
125
+ export async function getActiveDrivers(driverConfig, maxParallelImports, useDrivers = []) {
126
+ /** @type {string[]} */
127
+ let filteredDriverNames = [];
128
+ if (_.isEmpty(useDrivers)) {
129
+ // load all drivers if none are requested
130
+ filteredDriverNames = _.keys(driverConfig.installedExtensions);
131
+ } else {
132
+ // Load drivers in the same order that was used while enumerating them
133
+ for (const driverName of useDrivers) {
134
+ if (driverName in driverConfig.installedExtensions) {
135
+ filteredDriverNames.push(driverName);
136
+ } else {
137
+ const suffix = _.isEmpty(driverConfig.installedExtensions)
138
+ ? `You don't have any drivers installed yet.`
139
+ : `Only the following ${_.size(driverConfig.installedExtensions) === 1 ? `driver is` : `drivers are`} ` +
140
+ `available: ${_.keys(driverConfig.installedExtensions)}`;
141
+ throw new Error(
142
+ `Could not load the driver '${driverName}' because it is not installed. ${suffix}`
143
+ );
144
+ }
145
+ }
146
+ }
147
+ return new Map(await importExtensions('driver', driverConfig, filteredDriverNames, maxParallelImports));
111
148
  }
112
149
 
113
150
  /**
@@ -30,10 +30,9 @@ async function registerNode(data, addr, port, basePath) {
30
30
  try {
31
31
  data = JSON.parse(data);
32
32
  } catch (err) {
33
- logger.errorAndThrow(
33
+ throw logger.errorWithException(
34
34
  `Syntax error in node configuration file ${configFilePath}: ${err.message}`
35
35
  );
36
- return;
37
36
  }
38
37
  }
39
38