@appium/docutils 2.2.2 → 2.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.
Files changed (48) hide show
  1. package/build/lib/builder/deploy.d.ts +5 -5
  2. package/build/lib/builder/deploy.d.ts.map +1 -1
  3. package/build/lib/builder/deploy.js +41 -42
  4. package/build/lib/builder/deploy.js.map +1 -1
  5. package/build/lib/builder/site.d.ts +5 -5
  6. package/build/lib/builder/site.d.ts.map +1 -1
  7. package/build/lib/builder/site.js +23 -24
  8. package/build/lib/builder/site.js.map +1 -1
  9. package/build/lib/cli/check.d.ts.map +1 -1
  10. package/build/lib/cli/check.js +10 -10
  11. package/build/lib/cli/check.js.map +1 -1
  12. package/build/lib/cli/index.d.ts +4 -0
  13. package/build/lib/cli/index.d.ts.map +1 -1
  14. package/build/lib/cli/index.js +4 -0
  15. package/build/lib/cli/index.js.map +1 -1
  16. package/build/lib/fs.d.ts.map +1 -1
  17. package/build/lib/fs.js.map +1 -1
  18. package/build/lib/init.d.ts +12 -12
  19. package/build/lib/init.d.ts.map +1 -1
  20. package/build/lib/init.js.map +1 -1
  21. package/build/lib/logger.d.ts +5 -0
  22. package/build/lib/logger.d.ts.map +1 -1
  23. package/build/lib/logger.js +5 -0
  24. package/build/lib/logger.js.map +1 -1
  25. package/build/lib/scaffold.d.ts +10 -10
  26. package/build/lib/scaffold.d.ts.map +1 -1
  27. package/build/lib/scaffold.js +10 -10
  28. package/build/lib/scaffold.js.map +1 -1
  29. package/build/lib/util.d.ts +6 -1
  30. package/build/lib/util.d.ts.map +1 -1
  31. package/build/lib/util.js +15 -0
  32. package/build/lib/util.js.map +1 -1
  33. package/build/lib/validate.d.ts +42 -45
  34. package/build/lib/validate.d.ts.map +1 -1
  35. package/build/lib/validate.js +20 -20
  36. package/build/lib/validate.js.map +1 -1
  37. package/lib/builder/deploy.ts +121 -122
  38. package/lib/builder/site.ts +69 -70
  39. package/lib/cli/check.ts +13 -13
  40. package/lib/cli/index.ts +4 -0
  41. package/lib/fs.ts +2 -2
  42. package/lib/init.ts +54 -54
  43. package/lib/logger.ts +5 -0
  44. package/lib/scaffold.ts +94 -94
  45. package/lib/util.ts +20 -1
  46. package/lib/validate.ts +47 -51
  47. package/package.json +8 -8
  48. package/requirements.txt +3 -3
@@ -7,43 +7,62 @@
7
7
 
8
8
  import path from 'node:path';
9
9
  import type {TeenProcessExecOptions} from 'teen_process';
10
- import {exec} from 'teen_process';
11
10
  import {DEFAULT_SITE_DIR, NAME_BIN, NAME_MKDOCS, NAME_MKDOCS_YML} from '../constants';
12
11
  import {DocutilsError} from '../error';
13
12
  import {findMkDocsYml, isMkDocsInstalled, readMkDocsYml, requirePython} from '../fs';
14
13
  import {getLogger} from '../logger';
15
14
  import type {SpawnBackgroundProcessOpts} from '../util';
16
- import {relative, spawnBackgroundProcess, stopwatch} from '../util';
15
+ import {execWithErrorHandling, relative, spawnBackgroundProcess, stopwatch} from '../util';
17
16
 
18
17
  const log = getLogger('mkdocs');
19
18
 
20
19
  /**
21
- * Runs `mkdocs serve`
22
- * @param pythonPath Path to Python 3 executable
23
- * @param args Extra args to `mkdocs build`
24
- * @param opts Extra options for `teen_process.Subprocess.start`
25
- * @param mkDocsPath Path to `mkdocs` executable
20
+ * Options for {@linkcode buildSite}.
26
21
  */
27
- async function doServe(
28
- pythonPath: string,
29
- args: string[] = [],
30
- opts: SpawnBackgroundProcessOpts = {},
31
- ) {
32
- const finalArgs = ['-m', NAME_MKDOCS, 'serve', ...args];
33
- log.debug('Executing %s via: %s, %O', NAME_MKDOCS, pythonPath, finalArgs);
34
- return spawnBackgroundProcess(pythonPath, finalArgs, opts);
35
- }
22
+ export interface BuildMkDocsOpts {
23
+ /**
24
+ * Path to `mkdocs.yml`
25
+ */
26
+ mkdocsYml?: string;
36
27
 
37
- /**
38
- * Runs `mkdocs build`
39
- * @param pythonPath Path to Python 3 executable
40
- * @param args Extra args to `mkdocs build`
41
- * @param opts Extra options to `teen_process.exec`
42
- */
43
- async function doBuild(pythonPath: string, args: string[] = [], opts: TeenProcessExecOptions = {}) {
44
- const finalArgs = ['-m', NAME_MKDOCS, 'build', ...args];
45
- log.debug('Executing %s via: %s, %O', NAME_MKDOCS, pythonPath, finalArgs);
46
- return await exec(pythonPath, finalArgs, opts);
28
+ /**
29
+ * Path to output directory
30
+ */
31
+ siteDir?: string;
32
+
33
+ /**
34
+ * MkDocs theme to use
35
+ * @defaultValue 'mkdocs'
36
+ */
37
+ theme?: string;
38
+
39
+ /**
40
+ * Current working directory
41
+ * @defaultValue `process.cwd()`
42
+ */
43
+ cwd?: string;
44
+
45
+ /**
46
+ * Path to `package.json`
47
+ *
48
+ * Used to find `mkdocs.yml` if unspecified.
49
+ */
50
+ packageJson?: string;
51
+
52
+ /**
53
+ * If `true`, run `mkdocs serve` instead of `mkdocs build`
54
+ */
55
+ serve?: boolean;
56
+
57
+ /**
58
+ * Extra options for {@linkcode teen_process.exec}
59
+ */
60
+ execOpts?: TeenProcessExecOptions;
61
+
62
+ /**
63
+ * Extra options for {@linkcode spawnBackgroundProcess}
64
+ */
65
+ serveOpts?: SpawnBackgroundProcessOpts;
47
66
  }
48
67
 
49
68
  /**
@@ -107,50 +126,30 @@ export async function buildSite({
107
126
  }
108
127
 
109
128
  /**
110
- * Options for {@linkcode buildSite}.
129
+ * Runs `mkdocs serve`
130
+ * @param pythonPath Path to Python 3 executable
131
+ * @param args Extra args to `mkdocs build`
132
+ * @param opts Extra options for `teen_process.Subprocess.start`
133
+ * @param mkDocsPath Path to `mkdocs` executable
111
134
  */
112
- export interface BuildMkDocsOpts {
113
- /**
114
- * Path to `mkdocs.yml`
115
- */
116
- mkdocsYml?: string;
117
-
118
- /**
119
- * Path to output directory
120
- */
121
- siteDir?: string;
122
-
123
- /**
124
- * MkDocs theme to use
125
- * @defaultValue 'mkdocs'
126
- */
127
- theme?: string;
128
-
129
- /**
130
- * Current working directory
131
- * @defaultValue `process.cwd()`
132
- */
133
- cwd?: string;
134
-
135
- /**
136
- * Path to `package.json`
137
- *
138
- * Used to find `mkdocs.yml` if unspecified.
139
- */
140
- packageJson?: string;
141
-
142
- /**
143
- * If `true`, run `mkdocs serve` instead of `mkdocs build`
144
- */
145
- serve?: boolean;
146
-
147
- /**
148
- * Extra options for {@linkcode teen_process.exec}
149
- */
150
- execOpts?: TeenProcessExecOptions;
135
+ async function doServe(
136
+ pythonPath: string,
137
+ args: string[] = [],
138
+ opts: SpawnBackgroundProcessOpts = {},
139
+ ) {
140
+ const finalArgs = ['-m', NAME_MKDOCS, 'serve', ...args];
141
+ log.debug('Executing %s via: %s, %O', NAME_MKDOCS, pythonPath, finalArgs);
142
+ return spawnBackgroundProcess(pythonPath, finalArgs, opts);
143
+ }
151
144
 
152
- /**
153
- * Extra options for {@linkcode spawnBackgroundProcess}
154
- */
155
- serveOpts?: SpawnBackgroundProcessOpts;
145
+ /**
146
+ * Runs `mkdocs build`
147
+ * @param pythonPath Path to Python 3 executable
148
+ * @param args Extra args to `mkdocs build`
149
+ * @param opts Extra options to `teen_process.exec`
150
+ */
151
+ async function doBuild(pythonPath: string, args: string[] = [], opts: TeenProcessExecOptions = {}) {
152
+ const finalArgs = ['-m', NAME_MKDOCS, 'build', ...args];
153
+ log.debug('Executing %s via: %s, %O', NAME_MKDOCS, pythonPath, finalArgs);
154
+ return await execWithErrorHandling(pythonPath, finalArgs, opts);
156
155
  }
package/lib/cli/check.ts CHANGED
@@ -10,19 +10,6 @@ import {getLogger} from '../logger';
10
10
 
11
11
  const log = getLogger('check');
12
12
 
13
- /**
14
- * Given a list of objects with `id` and `path` props, filters out the ones that do not exist
15
- * @param paths Filepaths
16
- * @returns Missing files
17
- */
18
- async function filterMissing(paths: MissingFileData[]): Promise<MissingFileData[]> {
19
- const exists = await Promise.all(
20
- paths.map(async ({id, path}) => ({id, path, exists: await fs.exists(path)}))
21
- );
22
- const results = _.reject(exists, 'exists');
23
- return _.map(results, (result) => _.omit(result, 'exists'));
24
- }
25
-
26
13
  /**
27
14
  * Data structure describing a missing file; returned by {@linkcode filterMissing}
28
15
  */
@@ -88,3 +75,16 @@ export async function checkMissingPaths<T extends Record<string, Options>>(
88
75
 
89
76
  return true;
90
77
  }
78
+
79
+ /**
80
+ * Given a list of objects with `id` and `path` props, filters out the ones that do not exist
81
+ * @param paths Filepaths
82
+ * @returns Missing files
83
+ */
84
+ async function filterMissing(paths: MissingFileData[]): Promise<MissingFileData[]> {
85
+ const exists = await Promise.all(
86
+ paths.map(async ({id, path}) => ({id, path, exists: await fs.exists(path)}))
87
+ );
88
+ const results = _.reject(exists, 'exists');
89
+ return _.map(results, (result) => _.omit(result, 'exists'));
90
+ }
package/lib/cli/index.ts CHANGED
@@ -23,6 +23,10 @@ const pkg = readPackageSync({cwd: fs.findRoot(__dirname)});
23
23
  const log = getLogger('cli');
24
24
  const IMPLICATIONS_FAILED_REGEX = /implications\s+failed:\n\s*(.+)\s->\s(.+)$/i;
25
25
 
26
+ /**
27
+ * Entry point for the docutils CLI.
28
+ * @param argv Raw argv values (without node/bin by default).
29
+ */
26
30
  export async function main(argv = hideBin(process.argv)) {
27
31
  const config = await findConfig(argv);
28
32
 
package/lib/fs.ts CHANGED
@@ -140,6 +140,8 @@ export const readJson = _.memoize(
140
140
  JSON.parse(await fs.readFile(filepath, 'utf8')),
141
141
  );
142
142
 
143
+ type WhichFunction = (cmd: string, opts?: {nothrow: boolean}) => Promise<string | null>;
144
+
143
145
  /**
144
146
  * Writes contents to a file. Any JSON objects are stringified
145
147
  * @param filepath - Path to file
@@ -152,8 +154,6 @@ export function writeFileString(filepath: string, content: JsonValue) {
152
154
  });
153
155
  }
154
156
 
155
- type WhichFunction = (cmd: string, opts?: {nothrow: boolean}) => Promise<string | null>;
156
-
157
157
  /**
158
158
  * `which` with memoization
159
159
  */
package/lib/init.ts CHANGED
@@ -83,6 +83,60 @@ export const initMkDocs: ScaffoldTask<InitMkDocsOptions, MkDocsYml> = createScaf
83
83
  },
84
84
  );
85
85
 
86
+ /**
87
+ * Options for {@linkcode initMkDocs}
88
+ */
89
+ export interface InitMkDocsOptions extends ScaffoldTaskOptions {
90
+ copyright?: string;
91
+ repoName?: string;
92
+ repoUrl?: string;
93
+ siteDescription?: string;
94
+ siteName?: string;
95
+ }
96
+
97
+ export interface InitPythonOptions extends ScaffoldTaskOptions {
98
+ /**
99
+ * Path to `python` (v3.x) executable
100
+ */
101
+ pythonPath?: string;
102
+
103
+ /**
104
+ * If true, upgrade only
105
+ */
106
+ upgrade?: boolean;
107
+ }
108
+
109
+ /**
110
+ * Options for `init` command handler
111
+ *
112
+ * The props of the various "path" options are rewritten as `dest` for the scaffold tasks functions.
113
+ */
114
+ export type InitOptions = Simplify<
115
+ Omit<InitPythonOptions & InitMkDocsOptions, 'dest'> & {
116
+ /**
117
+ * If `true` will install Python deps
118
+ */
119
+ python?: boolean;
120
+ /**
121
+ * If `true` will initialize a `mkdocs.yml` file
122
+ */
123
+ mkdocs?: boolean;
124
+ /**
125
+ * Path to existing `package.json` file
126
+ */
127
+ packageJson?: string;
128
+ /**
129
+ * Path to new or existing `mkdocs.yml` file
130
+ */
131
+ mkdocsYml?: string;
132
+
133
+ /**
134
+ * If `true`, upgrade only
135
+ */
136
+ upgrade?: boolean;
137
+ }
138
+ >;
139
+
86
140
  /**
87
141
  * Installs Python dependencies
88
142
  * @param opts Options
@@ -127,17 +181,6 @@ export async function initPython({
127
181
  log.success('Successfully installed Python dependencies');
128
182
  }
129
183
 
130
- /**
131
- * Options for {@linkcode initMkDocs}
132
- */
133
- export interface InitMkDocsOptions extends ScaffoldTaskOptions {
134
- copyright?: string;
135
- repoName?: string;
136
- repoUrl?: string;
137
- siteDescription?: string;
138
- siteName?: string;
139
- }
140
-
141
184
  /**
142
185
  * Main handler for `init` command.
143
186
  *
@@ -177,46 +220,3 @@ export async function init({
177
220
  });
178
221
  }
179
222
  }
180
-
181
- export interface InitPythonOptions extends ScaffoldTaskOptions {
182
- /**
183
- * Path to `python` (v3.x) executable
184
- */
185
- pythonPath?: string;
186
-
187
- /**
188
- * If true, upgrade only
189
- */
190
- upgrade?: boolean;
191
- }
192
-
193
- /**
194
- * Options for `init` command handler
195
- *
196
- * The props of the various "path" options are rewritten as `dest` for the scaffold tasks functions.
197
- */
198
- export type InitOptions = Simplify<
199
- Omit<InitPythonOptions & InitMkDocsOptions, 'dest'> & {
200
- /**
201
- * If `true` will install Python deps
202
- */
203
- python?: boolean;
204
- /**
205
- * If `true` will initialize a `mkdocs.yml` file
206
- */
207
- mkdocs?: boolean;
208
- /**
209
- * Path to existing `package.json` file
210
- */
211
- packageJson?: string;
212
- /**
213
- * Path to new or existing `mkdocs.yml` file
214
- */
215
- mkdocsYml?: string;
216
-
217
- /**
218
- * If `true`, upgrade only
219
- */
220
- upgrade?: boolean;
221
- }
222
- >;
package/lib/logger.ts CHANGED
@@ -47,6 +47,11 @@ rootLogger.pauseLogs();
47
47
  */
48
48
  const loggers: Map<string, WeakRef<typeof ConsolaInstance>> = new Map();
49
49
 
50
+ /**
51
+ * Returns a tagged logger, creating and caching one if needed.
52
+ * @param tag Logger tag.
53
+ * @param parent Parent logger to derive from.
54
+ */
50
55
  export function getLogger(tag: string, parent = rootLogger) {
51
56
  if (loggers.has(tag)) {
52
57
  const logger = loggers.get(tag)?.deref();
package/lib/scaffold.ts CHANGED
@@ -18,26 +18,6 @@ import {NAME_ERR_ENOENT} from './constants';
18
18
  const log = getLogger('init');
19
19
  const dryRunLog = getLogger('dry-run', log);
20
20
 
21
- /**
22
- * Creates a unified patch for display in "dry run" mode
23
- * @param filename - File name to use
24
- * @param oldData - Old data
25
- * @param newData - New Data
26
- * @returns Patch string
27
- */
28
- function makePatch<T extends JsonValue>(
29
- filename: string,
30
- oldData: T | string,
31
- newData: T | string,
32
- serializer: ScaffoldTaskSerializer<T> = stringifyJson
33
- ) {
34
- return createPatch(
35
- filename,
36
- _.isString(oldData) ? oldData : serializer(oldData),
37
- _.isString(newData) ? newData : serializer(newData)
38
- );
39
- }
40
-
41
21
  /**
42
22
  * Options for a task which are not the {@link ScaffoldTaskOptions base options}
43
23
  */
@@ -55,6 +35,84 @@ export type ScaffoldTask<Opts extends ScaffoldTaskOptions, T extends JsonObject>
55
35
  opts: Opts
56
36
  ) => Promise<ScaffoldTaskResult<T>>;
57
37
 
38
+ /**
39
+ * Optional function which can be used to post-process the content of a file. Usually used to merge
40
+ * various options with existing content
41
+ */
42
+ export type ScaffoldTaskTransformer<Opts extends ScaffoldTaskOptions, T extends JsonValue> = (
43
+ content: Readonly<T>,
44
+ opts: TaskSpecificOpts<Opts>,
45
+ pkg: Readonly<NormalizedPackageJson>
46
+ ) => T;
47
+
48
+ /**
49
+ * A function which deserializes a string into a JS value.
50
+ */
51
+ export type ScaffoldTaskDeserializer<T> = (content: string) => T;
52
+
53
+ /**
54
+ * A function which serializes a JS value into a string.
55
+ */
56
+ export type ScaffoldTaskSerializer<T> = (content: T) => string;
57
+
58
+ /**
59
+ * Options for {@linkcode createScaffoldTask}
60
+ */
61
+ export interface CreateScaffoldTaskOptions<Opts extends ScaffoldTaskOptions, T extends JsonValue> {
62
+ /**
63
+ * Transformer function
64
+ */
65
+ transform?: ScaffoldTaskTransformer<Opts, T>;
66
+ /**
67
+ * Deserializer function
68
+ */
69
+ deserialize?: ScaffoldTaskDeserializer<T>;
70
+ /**
71
+ * Serializer function
72
+ */
73
+ serialize?: ScaffoldTaskSerializer<T>;
74
+ }
75
+
76
+ /**
77
+ * Base options for all scaffold tasks
78
+ */
79
+ export interface ScaffoldTaskOptions {
80
+ /**
81
+ * Current working directory
82
+ */
83
+ cwd?: string;
84
+ /**
85
+ * Destination file
86
+ */
87
+ dest?: string;
88
+ /**
89
+ * If `true` will not write files
90
+ */
91
+ dryRun?: boolean;
92
+ /**
93
+ * If `true` will overwrite fields in `typedoc.json`
94
+ */
95
+ overwrite?: boolean;
96
+ /**
97
+ * Path to `package.json`
98
+ */
99
+ packageJson?: string;
100
+ }
101
+
102
+ /**
103
+ * The return value of a {@linkcode ScaffoldTask}
104
+ */
105
+ export interface ScaffoldTaskResult<T> {
106
+ /**
107
+ * The content of whatever it wrote or would write
108
+ */
109
+ content: T;
110
+ /**
111
+ * The filepath of whatever it wrote or would write
112
+ */
113
+ path: string;
114
+ }
115
+
58
116
  /**
59
117
  * Factory for a {@linkcode ScaffoldTask}.
60
118
  *
@@ -153,79 +211,21 @@ export function createScaffoldTask<Opts extends ScaffoldTaskOptions, T extends J
153
211
  }
154
212
 
155
213
  /**
156
- * Optional function which can be used to post-process the content of a file. Usually used to merge
157
- * various options with existing content
158
- */
159
- export type ScaffoldTaskTransformer<Opts extends ScaffoldTaskOptions, T extends JsonValue> = (
160
- content: Readonly<T>,
161
- opts: TaskSpecificOpts<Opts>,
162
- pkg: Readonly<NormalizedPackageJson>
163
- ) => T;
164
-
165
- /**
166
- * A function which deserializes a string into a JS value.
167
- */
168
- export type ScaffoldTaskDeserializer<T> = (content: string) => T;
169
-
170
- /**
171
- * A function which serializes a JS value into a string.
172
- */
173
- export type ScaffoldTaskSerializer<T> = (content: T) => string;
174
-
175
- /**
176
- * Options for {@linkcode createScaffoldTask}
177
- */
178
- export interface CreateScaffoldTaskOptions<Opts extends ScaffoldTaskOptions, T extends JsonValue> {
179
- /**
180
- * Transformer function
181
- */
182
- transform?: ScaffoldTaskTransformer<Opts, T>;
183
- /**
184
- * Deserializer function
185
- */
186
- deserialize?: ScaffoldTaskDeserializer<T>;
187
- /**
188
- * Serializer function
189
- */
190
- serialize?: ScaffoldTaskSerializer<T>;
191
- }
192
-
193
- /**
194
- * Base options for all scaffold tasks
195
- */
196
- export interface ScaffoldTaskOptions {
197
- /**
198
- * Current working directory
199
- */
200
- cwd?: string;
201
- /**
202
- * Destination file
203
- */
204
- dest?: string;
205
- /**
206
- * If `true` will not write files
207
- */
208
- dryRun?: boolean;
209
- /**
210
- * If `true` will overwrite fields in `typedoc.json`
211
- */
212
- overwrite?: boolean;
213
- /**
214
- * Path to `package.json`
215
- */
216
- packageJson?: string;
217
- }
218
-
219
- /**
220
- * The return value of a {@linkcode ScaffoldTask}
214
+ * Creates a unified patch for display in "dry run" mode
215
+ * @param filename - File name to use
216
+ * @param oldData - Old data
217
+ * @param newData - New Data
218
+ * @returns Patch string
221
219
  */
222
- export interface ScaffoldTaskResult<T> {
223
- /**
224
- * The content of whatever it wrote or would write
225
- */
226
- content: T;
227
- /**
228
- * The filepath of whatever it wrote or would write
229
- */
230
- path: string;
220
+ function makePatch<T extends JsonValue>(
221
+ filename: string,
222
+ oldData: T | string,
223
+ newData: T | string,
224
+ serializer: ScaffoldTaskSerializer<T> = stringifyJson
225
+ ) {
226
+ return createPatch(
227
+ filename,
228
+ _.isString(oldData) ? oldData : serializer(oldData),
229
+ _.isString(newData) ? newData : serializer(newData)
230
+ );
231
231
  }
package/lib/util.ts CHANGED
@@ -7,6 +7,8 @@ import _ from 'lodash';
7
7
  import type {SpawnOptions} from 'node:child_process';
8
8
  import {spawn} from 'node:child_process';
9
9
  import path from 'node:path';
10
+ import type {ExecError, TeenProcessExecOptions} from 'teen_process';
11
+ import {exec} from 'teen_process';
10
12
 
11
13
  /**
12
14
  * Computes a relative path, prepending `./`
@@ -71,6 +73,8 @@ export const argify: (obj: Record<string, string | number | boolean | undefined>
71
73
  _.flatten
72
74
  );
73
75
 
76
+ export type SpawnBackgroundProcessOpts = Omit<SpawnOptions, 'stdio'>;
77
+
74
78
  /**
75
79
  * Spawns a long-running "background" child process. This is expected to only return control to the
76
80
  * parent process in the case of a nonzero exit code from the child process.
@@ -95,4 +99,19 @@ export async function spawnBackgroundProcess(command: string, args: string[], op
95
99
  });
96
100
  }
97
101
 
98
- export type SpawnBackgroundProcessOpts = Omit<SpawnOptions, 'stdio'>;
102
+ /**
103
+ * Wraps {@linkcode exec} with error handling that appends stderr to the thrown error message.
104
+ */
105
+ export async function execWithErrorHandling(
106
+ cmd: string,
107
+ args?: string[],
108
+ opts?: TeenProcessExecOptions,
109
+ ) {
110
+ try {
111
+ return await exec(cmd, args, opts);
112
+ } catch (err) {
113
+ const execErr = err as ExecError;
114
+ execErr.message = execErr.stderr ? `${execErr.message}\nCommand error:\n${execErr.stderr}` : execErr.message;
115
+ throw execErr;
116
+ }
117
+ }