@percy/cli-command 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/command.js CHANGED
@@ -5,61 +5,68 @@ import { generatePromise, AbortController, AbortError } from '@percy/core/utils'
5
5
  import * as CoreConfig from '@percy/core/config';
6
6
  import * as builtInFlags from './flags.js';
7
7
  import formatHelp from './help.js';
8
- import parse from './parse.js'; // Copies a command definition and adds built-in flags and config options.
8
+ import parse from './parse.js';
9
9
 
10
+ // Copies a command definition and adds built-in flags and config options.
10
11
  function withBuiltIns(definition) {
11
- let def = { ...definition
12
+ let def = {
13
+ ...definition
12
14
  };
13
- def.flags = [...(def.flags ?? [])]; // ensure built-ins aren't already overridden
15
+ def.flags = [...(def.flags ?? [])];
14
16
 
17
+ // ensure built-ins aren't already overridden
15
18
  let addDedupedFlag = flag => {
16
19
  if (!def.flags.find(f => f.name === flag.name)) {
17
20
  let short = def.flags.find(f => f.short === flag.short);
18
- def.flags.push(short ? del({ ...flag
21
+ def.flags.push(short ? del({
22
+ ...flag
19
23
  }, 'short') : flag);
20
24
  }
21
- }; // add percy specific built-in flags
22
-
25
+ };
23
26
 
27
+ // add percy specific built-in flags
24
28
  if (def.percy && def.percy !== true) {
25
- builtInFlags.PERCY.forEach(addDedupedFlag); // maybe include percy server flags
29
+ builtInFlags.PERCY.forEach(addDedupedFlag);
26
30
 
31
+ // maybe include percy server flags
27
32
  if (def.percy.server === true) {
28
33
  builtInFlags.SERVER.forEach(addDedupedFlag);
29
- } // maybe include percy discovery flags
30
-
34
+ }
31
35
 
36
+ // maybe include percy discovery flags
32
37
  if (def.percy.skipDiscovery !== true) {
33
38
  builtInFlags.DISCOVERY.forEach(addDedupedFlag);
34
39
  }
35
- } // always add global built-in flags
36
-
40
+ }
37
41
 
38
- builtInFlags.GLOBAL.forEach(addDedupedFlag); // copy any existing config before adding to it
42
+ // always add global built-in flags
43
+ builtInFlags.GLOBAL.forEach(addDedupedFlag);
39
44
 
40
- def.config = { ...def.config
45
+ // copy any existing config before adding to it
46
+ def.config = {
47
+ ...def.config
41
48
  };
42
49
  def.config.schemas = [...(def.config.schemas ?? [])];
43
- def.config.migrations = [...(def.config.migrations ?? [])]; // add percy specific built-in config options
50
+ def.config.migrations = [...(def.config.migrations ?? [])];
44
51
 
52
+ // add percy specific built-in config options
45
53
  if (def.percy) {
46
54
  def.config.schemas.unshift(CoreConfig.schemas);
47
55
  def.config.migrations.unshift(CoreConfig.migrations);
48
56
  }
49
-
50
57
  return def;
51
- } // Helper to throw an error with an exit code and optional reason message
52
-
58
+ }
53
59
 
60
+ // Helper to throw an error with an exit code and optional reason message
54
61
  function exit(exitCode, reason = '') {
55
62
  let err = reason instanceof Error ? reason : new Error(reason);
56
63
  throw Object.assign(err, {
57
64
  exitCode
58
65
  });
59
- } // Runs the parsed command callback with a contextual argument consisting of specific parsed input
60
- // and other common command helpers and properties.
61
-
66
+ }
62
67
 
68
+ // Runs the parsed command callback with a contextual argument consisting of specific parsed input
69
+ // and other common command helpers and properties.
63
70
  async function runCommandWithContext(parsed) {
64
71
  let {
65
72
  command,
@@ -67,8 +74,8 @@ async function runCommandWithContext(parsed) {
67
74
  args,
68
75
  argv,
69
76
  log
70
- } = parsed; // include flags, args, argv, logger, exit helper, and env info
71
-
77
+ } = parsed;
78
+ // include flags, args, argv, logger, exit helper, and env info
72
79
  let context = {
73
80
  flags,
74
81
  args,
@@ -78,13 +85,15 @@ async function runCommandWithContext(parsed) {
78
85
  };
79
86
  let env = context.env = process.env;
80
87
  let pkg = command.packageInformation;
81
- let def = command.definition; // automatically include a preconfigured percy instance
88
+ let def = command.definition;
82
89
 
90
+ // automatically include a preconfigured percy instance
83
91
  if (def.percy) {
84
92
  let {
85
93
  Percy
86
- } = await import('@percy/core'); // shallow merge with default options
94
+ } = await import('@percy/core');
87
95
 
96
+ // shallow merge with default options
88
97
  let conf = {
89
98
  server: false,
90
99
  ...def.percy
@@ -93,60 +102,55 @@ async function runCommandWithContext(parsed) {
93
102
  conf.environmentInfo || (conf.environmentInfo = `node/${process.version}`);
94
103
  Object.defineProperty(context, 'percy', {
95
104
  configurable: true,
96
-
97
105
  get() {
98
106
  // percy is disabled, do not return an instance
99
- if (env.PERCY_ENABLE === '0') return; // redefine the context property once configured
107
+ if (env.PERCY_ENABLE === '0') return;
100
108
 
109
+ // redefine the context property once configured
101
110
  Object.defineProperty(context, 'percy', {
102
111
  // map and merge percy arguments with config options
103
112
  value: new Percy([...parsed.operators.entries()].reduce((conf, [opt, value]) => opt.percyrc ? set(conf, opt.percyrc, value) : conf, conf))
104
113
  });
105
114
  return context.percy;
106
115
  }
107
-
108
116
  });
109
- } // process signals will abort
110
-
117
+ }
111
118
 
119
+ // process signals will abort
112
120
  let ctrl = new AbortController();
113
121
  let signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'].map(signal => {
114
122
  let handler = () => ctrl.abort(new AbortError(signal, {
115
123
  signal,
116
124
  exitCode: 0
117
125
  }));
118
-
119
126
  handler.off = () => process.off(signal, handler);
120
-
121
127
  process.on(signal, handler);
122
128
  return handler;
123
- }); // run the command callback with context and cleanup handlers after
129
+ });
124
130
 
131
+ // run the command callback with context and cleanup handlers after
125
132
  await generatePromise(command.callback(context), ctrl.signal, error => {
126
133
  for (let handler of signals) handler.off();
127
-
128
134
  if (error) throw error;
129
135
  });
130
- } // Returns a command runner function that when run will parse provided command-line options and run
136
+ }
137
+
138
+ // Returns a command runner function that when run will parse provided command-line options and run
131
139
  // the parsed command callback. The returned runner will automatically output version and help
132
140
  // information when requested, and handle any thrown errors exiting when appropriate.
133
-
134
-
135
141
  export function command(name, definition, callback) {
136
- definition = withBuiltIns(definition); // auto register config schemas and migrations
142
+ definition = withBuiltIns(definition);
137
143
 
144
+ // auto register config schemas and migrations
138
145
  PercyConfig.addSchema(definition.config.schemas);
139
146
  PercyConfig.addMigration(definition.config.migrations);
140
-
141
147
  async function runner(argv = []) {
142
148
  // reset loglevel for testing
143
149
  logger.loglevel('info');
144
150
  let log = logger('cli');
145
-
146
151
  try {
147
152
  // parse input
148
153
  let parsed = await parse(runner, argv);
149
-
150
154
  if (parsed.version) {
151
155
  // version requested
152
156
  log.stdout.write(parsed.command.definition.version + '\n');
@@ -161,24 +165,23 @@ export function command(name, definition, callback) {
161
165
  // auto log unhandled error messages
162
166
  if (err.message && !err.signal) {
163
167
  if (err.exitCode === 0) log.warn(err.message);else log.error(err);
164
- } // exit when appropriate
165
-
168
+ }
166
169
 
170
+ // exit when appropriate
167
171
  if (err.exitCode !== 0) {
168
172
  err.exitCode ?? (err.exitCode = 1);
169
173
  err.message || (err.message = `EEXIT: ${err.exitCode}`);
170
-
171
174
  if (definition.exitOnError) {
172
175
  process.exit(err.exitCode);
173
- } // re-throw when not exiting
174
-
176
+ }
175
177
 
178
+ // re-throw when not exiting
176
179
  throw err;
177
180
  }
178
181
  }
179
- } // define command meta information
180
-
182
+ }
181
183
 
184
+ // define command meta information
182
185
  Object.defineProperties(runner, {
183
186
  name: {
184
187
  enumerable: true,
package/dist/flags.js CHANGED
@@ -1,5 +1,6 @@
1
- import logger from '@percy/logger'; // Global CLI flags
1
+ import logger from '@percy/logger';
2
2
 
3
+ // Global CLI flags
3
4
  export const verbose = {
4
5
  name: 'verbose',
5
6
  description: 'Log everything',
@@ -23,8 +24,9 @@ export const silent = {
23
24
  parse: () => !!logger.loglevel('silent'),
24
25
  group: 'Global',
25
26
  short: 's'
26
- }; // Common percy and asset discovery flags
27
+ };
27
28
 
29
+ // Common percy and asset discovery flags
28
30
  export const config = {
29
31
  name: 'config',
30
32
  description: 'Config file path',
@@ -89,11 +91,13 @@ export const debug = {
89
91
  parse: () => !!logger.loglevel('debug'),
90
92
  percyrc: 'skipUploads',
91
93
  group: 'Percy'
92
- }; // Group constants
94
+ };
93
95
 
96
+ // Group constants
94
97
  export const GLOBAL = [verbose, quiet, silent];
95
98
  export const PERCY = [config, dryRun];
96
99
  export const SERVER = [port];
97
- export const DISCOVERY = [allowedHostnames, disallowedHostnames, networkIdleTimeout, disableCache, debug]; // export the namespace as the default
100
+ export const DISCOVERY = [allowedHostnames, disallowedHostnames, networkIdleTimeout, disableCache, debug];
98
101
 
102
+ // export the namespace as the default
99
103
  export * as default from './flags.js';
package/dist/help.js CHANGED
@@ -1,30 +1,31 @@
1
- import logger from '@percy/logger'; // Helper to return an argument's description including it's default value.
1
+ import logger from '@percy/logger';
2
2
 
3
+ // Helper to return an argument's description including it's default value.
3
4
  function argumentDescription(arg) {
4
5
  let desc = arg.description || '';
5
-
6
6
  if (arg.default != null) {
7
7
  desc += ` (default: ${JSON.stringify(arg.default)})`;
8
8
  }
9
-
10
9
  return desc.trim();
11
- } // Helper to return an array of visible argument usage-description pairs.
12
-
10
+ }
13
11
 
12
+ // Helper to return an array of visible argument usage-description pairs.
14
13
  async function visibleArguments(command) {
15
- let args = command.definition.args ?? []; // not hidden and not deprecated
14
+ let args = command.definition.args ?? [];
16
15
 
16
+ // not hidden and not deprecated
17
17
  let visible = args.reduce((visible, arg) => {
18
18
  if (arg.hidden || arg.deprecated) return visible;
19
19
  visible.push([arg.name, argumentDescription(arg)]);
20
20
  return visible;
21
- }, []); // do not return any arguments if none have descriptions
21
+ }, []);
22
22
 
23
+ // do not return any arguments if none have descriptions
23
24
  if (!visible.some(([, desc]) => !!desc)) return [];
24
25
  return visible;
25
- } // Helper to return a command's usage string.
26
-
26
+ }
27
27
 
28
+ // Helper to return a command's usage string.
28
29
  function commandUsage(command) {
29
30
  let {
30
31
  name,
@@ -35,70 +36,68 @@ function commandUsage(command) {
35
36
  args,
36
37
  commands
37
38
  } = definition;
38
-
39
39
  if (!command.callback && commands !== null && commands !== void 0 && commands.length) {
40
40
  // if there are subcommands but no callback show subcommand usage
41
41
  usage || (usage = '<command>');
42
42
  } else {
43
43
  var _args, _args2;
44
-
45
44
  // show options followed by arguments
46
45
  args = (_args = args) === null || _args === void 0 ? void 0 : _args.filter(a => !a.hidden && !a.deprecated).map(a => a.required ? `<${a.name}>` : `[${a.name}]`);
47
46
  usage || (usage = `[options] ${((_args2 = args) === null || _args2 === void 0 ? void 0 : _args2.join(' ')) || ''}`);
48
47
  }
49
-
50
48
  return `${name} ${usage}`.trim();
51
- } // Helper to return an array of visible command usage-description pairs.
52
-
49
+ }
53
50
 
51
+ // Helper to return an array of visible command usage-description pairs.
54
52
  async function visibleCommands(command, nested) {
55
53
  let commands = command.definition.commands ?? [];
56
54
  if (typeof commands === 'function') commands = await commands();
57
55
  let visible = [];
58
-
59
56
  for (let cmd of commands) {
60
57
  // skip hidden commands entirely
61
- if (cmd.definition.hidden) continue; // create parent references and format subcommand names
58
+ if (cmd.definition.hidden) continue;
62
59
 
60
+ // create parent references and format subcommand names
63
61
  let name = command.parent ? `${command.name}:${cmd.name}` : cmd.name;
64
- cmd = { ...cmd,
62
+ cmd = {
63
+ ...cmd,
65
64
  name,
66
65
  parent: command
67
- }; // commands without callbacks are not visible themselves
66
+ };
68
67
 
68
+ // commands without callbacks are not visible themselves
69
69
  if (cmd.callback) {
70
70
  let {
71
71
  description
72
72
  } = cmd.definition;
73
73
  visible.push([commandUsage(cmd), description]);
74
- } // include visible subcommands, but not deeply nested subcommands
75
-
74
+ }
76
75
 
76
+ // include visible subcommands, but not deeply nested subcommands
77
77
  if (!nested && cmd.definition.commands) {
78
78
  visible.push(...(await visibleCommands(cmd, true)));
79
79
  }
80
- } // include implicit help if there are visible commands
81
-
80
+ }
82
81
 
82
+ // include implicit help if there are visible commands
83
83
  if (!nested && visible.length) {
84
84
  visible.push(['help [command]', 'Display command help']);
85
85
  }
86
-
87
86
  return visible;
88
- } // Helper to return an flags's description including it's default value.
89
-
87
+ }
90
88
 
89
+ // Helper to return an flags's description including it's default value.
91
90
  function flagDescription(flag) {
92
- let desc = flag.description || ''; // a boolean that defaults to false will be displayed with a --no prefix
91
+ let desc = flag.description || '';
93
92
 
93
+ // a boolean that defaults to false will be displayed with a --no prefix
94
94
  if (flag.type && flag.type !== 'boolean' && flag.default != null) {
95
95
  desc += ` (default: ${JSON.stringify(flag.default)})`;
96
96
  }
97
-
98
97
  return desc.trim();
99
- } // Helper to return a flag's usage string.
100
-
98
+ }
101
99
 
100
+ // Helper to return a flag's usage string.
102
101
  export function flagUsage(flag) {
103
102
  let {
104
103
  short,
@@ -106,33 +105,34 @@ export function flagUsage(flag) {
106
105
  type
107
106
  } = flag;
108
107
  let term = flag.name ? `--${flag.name}` : '';
109
-
110
108
  if (type && type !== 'boolean') {
111
109
  // if there is a default it is not required
112
110
  usage || (usage = flag.default != null ? `[${type}]` : `<${type}>`);
113
111
  } else if (flag.default === true) {
114
112
  // negated booleans have a prefix
115
113
  term && (term = `--no-${flag.name}`);
116
- } // add short flag usage
117
-
114
+ }
118
115
 
116
+ // add short flag usage
119
117
  if (short) term = `-${short}${term ? `, ${term}` : ''}`;
120
118
  return `${term} ${usage || ''}`.trim();
121
- } // Helper to return an array of visible flag usage-description pairs. A third entry is also included
122
- // with the returned pair representing any group the flag should belong to.
119
+ }
123
120
 
121
+ // Helper to return an array of visible flag usage-description pairs. A third entry is also included
122
+ // with the returned pair representing any group the flag should belong to.
124
123
  async function visibleFlags(command) {
125
- let flags = command.definition.flags; // not hidden and not deprecated
124
+ let flags = command.definition.flags;
126
125
 
126
+ // not hidden and not deprecated
127
127
  let visible = flags.reduce((visible, flag) => {
128
128
  if (flag.hidden || flag.deprecated) return visible;
129
129
  visible.push([flagUsage(flag), flagDescription(flag), flag.group]);
130
130
  return visible;
131
- }, []); // maybe hide the implicit help long or short flag (or both)
131
+ }, []);
132
132
 
133
+ // maybe hide the implicit help long or short flag (or both)
133
134
  let showLongHelpFlag = !flags.some(f => f.name === 'help');
134
135
  let showShortHelpFlag = !flags.some(f => f.short === 'h');
135
-
136
136
  if (showLongHelpFlag || showShortHelpFlag) {
137
137
  let help = {
138
138
  name: 'help',
@@ -142,12 +142,10 @@ async function visibleFlags(command) {
142
142
  if (!showShortHelpFlag) delete help.short;
143
143
  visible.push([flagUsage(help), 'Display command help', 'Global']);
144
144
  }
145
-
146
145
  if (command.definition.version) {
147
146
  // maybe hide the implicit version long or short flag (or both)
148
147
  let showLongVerFlag = !flags.some(f => f.name === 'version');
149
148
  let showShortVerFlag = !flags.some(f => f.short === 'V');
150
-
151
149
  if (showLongVerFlag || showShortVerFlag) {
152
150
  let ver = {
153
151
  name: 'version',
@@ -158,33 +156,31 @@ async function visibleFlags(command) {
158
156
  visible.push([flagUsage(ver), 'Display version', 'Global']);
159
157
  }
160
158
  }
161
-
162
159
  return visible;
163
- } // Helper to wrap a string at a length by inserting a newline between every segment.
164
-
160
+ }
165
161
 
162
+ // Helper to wrap a string at a length by inserting a newline between every segment.
166
163
  function wrapString(str, len) {
167
164
  let regex = new RegExp(`.{1,${len - 1}}(\\s|$)|\\S+?(\\s|$)`, 'g');
168
165
  return str.replace(regex, s => s.replace(/\s*\n?$/, str.endsWith(s) ? '' : '\n'));
169
- } // Helper to format an option usage-description pair into a single string where the description
170
- // wraps within the available space beside the usage string.
171
-
166
+ }
172
167
 
168
+ // Helper to format an option usage-description pair into a single string where the description
169
+ // wraps within the available space beside the usage string.
173
170
  function formatOptionHelp(option, usageWidth, maxWidth) {
174
- if (!option[1]) return option[0]; // calculate the available description width, at minimum 40
175
-
176
- let descWidth = Math.max(maxWidth - usageWidth, 40); // pad usage up to the description and indent the description
177
-
171
+ if (!option[1]) return option[0];
172
+ // calculate the available description width, at minimum 40
173
+ let descWidth = Math.max(maxWidth - usageWidth, 40);
174
+ // pad usage up to the description and indent the description
178
175
  return option[0].padEnd(usageWidth) + wrapString(option[1], descWidth).replace(/\n+/gm, `$&${' '.repeat(usageWidth)}`);
179
- } // Returns a formatted string displaying help output for the specified command. The help output
176
+ }
177
+
178
+ // Returns a formatted string displaying help output for the specified command. The help output
180
179
  // total width is determined by the logger's stdout TTY columns. Help output includes the command
181
180
  // description, usage, arguments, subcommand usage and descriptions, flag and flag group usage and
182
181
  // descriptions, and any command examples.
183
-
184
-
185
182
  export async function formatHelp(command) {
186
183
  var _examples, _examples2;
187
-
188
184
  let {
189
185
  name,
190
186
  parent: top,
@@ -194,52 +190,61 @@ export async function formatHelp(command) {
194
190
  description,
195
191
  examples
196
192
  } = definition;
197
- let output = []; // collect visible items first to determine max usage width
193
+ let output = [];
198
194
 
195
+ // collect visible items first to determine max usage width
199
196
  let args = await visibleArguments(command);
200
197
  let commands = await visibleCommands(command);
201
- let flags = await visibleFlags(command); // maximum help output total width
198
+ let flags = await visibleFlags(command);
202
199
 
203
- let maxWidth = logger.stdout.columns || 100; // calculate the longest usage width
204
-
205
- let usageWidth = Math.max(args.reduce((max, [usage]) => Math.max(max, usage.length), 0), commands.reduce((max, [usage]) => Math.max(max, usage.length), 0), flags.reduce((max, [usage]) => Math.max(max, usage.length), 0)); // format lists of options and add appropriate indentation
206
-
207
- let formatList = items => items.map(item => Array.isArray(item) ? // indent items and separate columns by 2 spaces each
208
- formatOptionHelp(item, usageWidth + 2, maxWidth - 2) : item).join('\n').replace(/^(?!$)/gm, ' '.repeat(2)); // format the command name to include the topmost command name
200
+ // maximum help output total width
201
+ let maxWidth = logger.stdout.columns || 100;
202
+ // calculate the longest usage width
203
+ let usageWidth = Math.max(args.reduce((max, [usage]) => Math.max(max, usage.length), 0), commands.reduce((max, [usage]) => Math.max(max, usage.length), 0), flags.reduce((max, [usage]) => Math.max(max, usage.length), 0));
209
204
 
205
+ // format lists of options and add appropriate indentation
206
+ let formatList = items => items.map(item => Array.isArray(item) ?
207
+ // indent items and separate columns by 2 spaces each
208
+ formatOptionHelp(item, usageWidth + 2, maxWidth - 2) : item).join('\n').replace(/^(?!$)/gm, ' '.repeat(2));
210
209
 
210
+ // format the command name to include the topmost command name
211
211
  while ((_top = top) !== null && _top !== void 0 && _top.parent) {
212
212
  var _top;
213
-
214
213
  top = top.parent;
215
214
  }
216
-
217
215
  if (top) name = `${top.name} ${name}`;
218
- command = { ...command,
216
+ command = {
217
+ ...command,
219
218
  name
220
- }; // include command description and own usage
219
+ };
221
220
 
221
+ // include command description and own usage
222
222
  if (description) output.push(wrapString(description, maxWidth), '');
223
- output.push('Usage:', formatList([`$ ${commandUsage(command)}`]), ''); // include command arguments
223
+ output.push('Usage:', formatList([`$ ${commandUsage(command)}`]), '');
224
224
 
225
- if (args.length) output.push('Arguments:', formatList(args), ''); // include command subcommands
225
+ // include command arguments
226
+ if (args.length) output.push('Arguments:', formatList(args), '');
226
227
 
228
+ // include command subcommands
227
229
  let commandGroupName = command.parent ? 'Subcommands:' : 'Commands:';
228
- if (commands.length) output.push(commandGroupName, formatList(commands), ''); // separate flags into groups
230
+ if (commands.length) output.push(commandGroupName, formatList(commands), '');
229
231
 
232
+ // separate flags into groups
230
233
  let groups = flags.reduce((all, option) => {
231
234
  let group = option[2] ? `${option[2]} options` : 'Options';
232
235
  return all.set(group, [...(all.get(group) || []), option]);
233
- }, new Map([['Options', []]])); // include each group of command flags
236
+ }, new Map([['Options', []]]));
234
237
 
238
+ // include each group of command flags
235
239
  for (let [group, flags] of groups.entries()) {
236
240
  if (flags.length) output.push(`${group}:`, formatList(flags), '');
237
- } // include command examples and replace '$0' with the command name
238
-
241
+ }
239
242
 
243
+ // include command examples and replace '$0' with the command name
240
244
  examples = (_examples = examples) === null || _examples === void 0 ? void 0 : _examples.map(ex => ex.replace('$0', `$ ${name}`));
241
- if ((_examples2 = examples) !== null && _examples2 !== void 0 && _examples2.length) output.push('Examples:', formatList(examples), ''); // join each line of output
245
+ if ((_examples2 = examples) !== null && _examples2 !== void 0 && _examples2.length) output.push('Examples:', formatList(examples), '');
242
246
 
247
+ // join each line of output
243
248
  return output.join('\n');
244
249
  }
245
250
  export default formatHelp;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { default, command } from './command.js';
2
- export { legacyCommand, legacyFlags as flags } from './legacy.js'; // export common packages to avoid dependency resolution issues
3
-
2
+ export { legacyCommand, legacyFlags as flags } from './legacy.js';
3
+ // export common packages to avoid dependency resolution issues
4
4
  export { default as PercyConfig } from '@percy/config';
5
5
  export { default as logger } from '@percy/logger';
package/dist/legacy.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { merge } from '@percy/config/utils';
2
- import { command } from './command.js'; // Legacy flags for older commands that inadvertently import a newer @percy/cli-command
2
+ import { command } from './command.js';
3
3
 
4
+ // Legacy flags for older commands that inadvertently import a newer @percy/cli-command
4
5
  export const legacyFlags = {
5
6
  config: {},
6
7
  logging: {},
@@ -19,34 +20,36 @@ export const legacyFlags = {
19
20
  type: 'integer',
20
21
  ...def
21
22
  })
22
- }; // Maps a legacy arg that might have alternate properties
23
+ };
23
24
 
25
+ // Maps a legacy arg that might have alternate properties
24
26
  function legacyArg(legacy) {
25
27
  let {
26
28
  name,
27
29
  ...def
28
- } = legacy; // do not camelcase legacy attribute names
29
-
30
- def.attribute = name; // add name back to mapped options
31
-
30
+ } = legacy;
31
+ // do not camelcase legacy attribute names
32
+ def.attribute = name;
33
+ // add name back to mapped options
32
34
  return {
33
35
  name,
34
36
  ...def
35
37
  };
36
- } // Maps a legacy flag that might have alternate properties
37
-
38
+ }
38
39
 
40
+ // Maps a legacy flag that might have alternate properties
39
41
  function legacyFlag(name, legacy) {
40
42
  let {
41
43
  char: short,
42
44
  deprecated,
43
45
  ...def
44
- } = legacy; // generic legacy type looks odd in help output
45
-
46
- if (def.type === 'option') def.type = 'value'; // do not camelcase legacy attribute names
47
-
48
- def.attribute = name; // transform legacy deprecated syntax
46
+ } = legacy;
47
+ // generic legacy type looks odd in help output
48
+ if (def.type === 'option') def.type = 'value';
49
+ // do not camelcase legacy attribute names
50
+ def.attribute = name;
49
51
 
52
+ // transform legacy deprecated syntax
50
53
  if (deprecated) {
51
54
  let {
52
55
  until,
@@ -54,20 +57,19 @@ function legacyFlag(name, legacy) {
54
57
  alt
55
58
  } = deprecated !== true ? deprecated : {};
56
59
  def.deprecated = [until, map ? `--${map}` : alt];
57
- } // add name and mapped short option
58
-
60
+ }
59
61
 
62
+ // add name and mapped short option
60
63
  return {
61
64
  name,
62
65
  short,
63
66
  ...def
64
67
  };
65
- } // Maps a legacy class-based command to the new functional syntax
66
-
68
+ }
67
69
 
70
+ // Maps a legacy class-based command to the new functional syntax
68
71
  export function legacyCommand(name, constructor) {
69
72
  var _constructor$args, _constructor$flags;
70
-
71
73
  let runner = command(name, {
72
74
  description: constructor.description,
73
75
  examples: constructor.examples,
@@ -81,23 +83,26 @@ export function legacyCommand(name, constructor) {
81
83
  legacy: true
82
84
  }, async function* (ctx) {
83
85
  let instance = new constructor();
84
- let error; // not an instance of the constructor, create one ourself
86
+ let error;
85
87
 
88
+ // not an instance of the constructor, create one ourself
86
89
  if (!(instance instanceof constructor)) {
87
90
  instance = Object.create(constructor.prototype);
88
- } // alias legacy global flags attribute names when necessary
89
-
91
+ }
90
92
 
91
- if (ctx.flags.dryRun) ctx.flags['dry-run'] = ctx.flags.dryRun; // legacy percyrc value
93
+ // alias legacy global flags attribute names when necessary
94
+ if (ctx.flags.dryRun) ctx.flags['dry-run'] = ctx.flags.dryRun;
92
95
 
96
+ // legacy percyrc value
93
97
  let percyrc = overrides => merge([ctx.percy.config, overrides, {
94
98
  skipUploads: ctx.flags.debug,
95
99
  dryRun: ctx.flags['dry-run'],
96
100
  config: false
97
- }], (path, prev, next) => // do not merge arrays
98
- Array.isArray(next) && [path, next]); // augment legacy instance properties
99
-
101
+ }], (path, prev, next) =>
102
+ // do not merge arrays
103
+ Array.isArray(next) && [path, next]);
100
104
 
105
+ // augment legacy instance properties
101
106
  Object.defineProperties(instance, {
102
107
  isPercyEnabled: {
103
108
  value: () => !!ctx.percy
@@ -123,39 +128,36 @@ export function legacyCommand(name, constructor) {
123
128
  log: {
124
129
  value: ctx.log
125
130
  }
126
- }); // legacy signal handling
131
+ });
127
132
 
133
+ // legacy signal handling
128
134
  let signals = ['SIGHUP', 'SIGINT', 'SIGTERM'];
129
-
130
135
  let cleanup = () => {
131
136
  var _instance$finally, _instance;
132
-
133
137
  return (_instance$finally = (_instance = instance).finally) === null || _instance$finally === void 0 ? void 0 : _instance$finally.call(_instance, new Error('SIGTERM'));
134
138
  };
139
+ for (let e of signals) process.on(e, cleanup);
135
140
 
136
- for (let e of signals) process.on(e, cleanup); // run and handle legacy command
137
-
138
-
141
+ // run and handle legacy command
139
142
  try {
140
143
  yield instance.run();
141
144
  } catch (err) {
142
145
  throw error = err;
143
146
  } finally {
144
147
  var _instance$finally2, _instance2;
145
-
146
148
  await ((_instance$finally2 = (_instance2 = instance).finally) === null || _instance$finally2 === void 0 ? void 0 : _instance$finally2.call(_instance2, error));
147
-
148
149
  for (let e of signals) process.off(e, cleanup);
149
150
  }
150
- }); // throw legacy errors for test compatibility
151
+ });
151
152
 
153
+ // throw legacy errors for test compatibility
152
154
  return Object.defineProperties(args => runner(args).catch(e => {
153
155
  throw new Error(`EEXIT: ${e.exitCode}`);
154
156
  }), Object.getOwnPropertyDescriptors(runner));
155
- } // Support the legacy static run method
157
+ }
156
158
 
159
+ // Support the legacy static run method
157
160
  command.run = function run(args) {
158
161
  return legacyCommand('legacy', this)(args);
159
162
  };
160
-
161
163
  export default legacyCommand;
package/dist/parse.js CHANGED
@@ -1,194 +1,195 @@
1
1
  import logger from '@percy/logger';
2
2
  import { camelcase } from '@percy/config/utils';
3
- import { flagUsage } from './help.js'; // Make it possible to identify parse errors.
3
+ import { flagUsage } from './help.js';
4
4
 
5
+ // Make it possible to identify parse errors.
5
6
  export class ParseError extends Error {
6
7
  name = this.constructor.name;
7
- } // Helper that makes it possible to throw parse errors without throw or new keywords.
8
+ }
8
9
 
10
+ // Helper that makes it possible to throw parse errors without throw or new keywords.
9
11
  function parseError(msg) {
10
12
  throw new ParseError(msg);
11
- } // Helper to identify a potential command-line option as a flag.
12
-
13
+ }
13
14
 
15
+ // Helper to identify a potential command-line option as a flag.
14
16
  function isFlag(option) {
15
17
  // not a flag if it doesn't start with a dash
16
- if (!option.startsWith('-')) return false; // safe to say it's a flag without a space
17
-
18
- if (!option.includes(' ')) return true; // containing a space with no operator is not a proper flag
19
-
20
- if (!option.includes('=')) return false; // as long as the operator precedes the space, it is a flag
21
-
18
+ if (!option.startsWith('-')) return false;
19
+ // safe to say it's a flag without a space
20
+ if (!option.includes(' ')) return true;
21
+ // containing a space with no operator is not a proper flag
22
+ if (!option.includes('=')) return false;
23
+ // as long as the operator precedes the space, it is a flag
22
24
  return option.indexOf('=') < option.indexOf(' ');
23
- } // Parses input to identify a command. Consumes input and adds to parsed output as needed, returning
25
+ }
26
+
27
+ // Parses input to identify a command. Consumes input and adds to parsed output as needed, returning
24
28
  // true if any defined command was found. A command is only identified via leading unparsed input
25
29
  // options. If a command is found, parsed output is updated accordingly and the current input option
26
30
  // is removed from further processing. An implicit help command is also supported.
27
-
28
-
29
31
  async function maybeParseCommand(input, parsed) {
30
32
  var _input$;
31
-
32
33
  // a flag, no defined commands, or already parsed other options
33
34
  if (isFlag(input[0]) || !parsed.command.definition.commands || parsed.operators.size || parsed.argv.length) {
34
35
  return;
35
- } // implicit help
36
-
36
+ }
37
37
 
38
+ // implicit help
38
39
  if (input[0] === 'help') {
39
40
  parsed.help = true;
40
41
  input.shift();
41
- } // nested commands can be a single argument with command names separated by colons (:)
42
-
42
+ }
43
43
 
44
+ // nested commands can be a single argument with command names separated by colons (:)
44
45
  let names = ((_input$ = input[0]) === null || _input$ === void 0 ? void 0 : _input$.split(':')) ?? [];
45
46
  let command;
46
-
47
47
  for (let name of names) {
48
48
  var _parent$definition$co, _command;
49
+ let parent = command || parsed.command;
49
50
 
50
- let parent = command || parsed.command; // find any nested command by name
51
-
51
+ // find any nested command by name
52
52
  command = (_parent$definition$co = parent.definition.commands) === null || _parent$definition$co === void 0 ? void 0 : _parent$definition$co.find(cmd => cmd.name === name);
53
- if (!command) break; // maintain nested names and parent relationships
53
+ if (!command) break;
54
54
 
55
+ // maintain nested names and parent relationships
55
56
  name = parent.parent ? `${parent.name}:${command.name}` : command.name;
56
57
  command = await normalizeCommand(command, {
57
58
  name,
58
59
  parent
59
- }); // some hidden commands should produce warnings when used
60
+ });
60
61
 
62
+ // some hidden commands should produce warnings when used
61
63
  if (typeof ((_command = command) === null || _command === void 0 ? void 0 : _command.definition.hidden) === 'string') {
62
64
  logger('cli').warn(`\nWarning: ${command.definition.hidden}\n`);
63
65
  }
64
66
  }
65
-
66
67
  if (command) {
67
68
  // update parsed properties and discard current input
68
69
  parsed.log = logger(`cli:${command.name}`);
69
70
  parsed.command = command;
70
71
  input.shift();
71
- } // help was requested, drain remaining input
72
-
72
+ }
73
73
 
74
+ // help was requested, drain remaining input
74
75
  if (parsed.help) {
75
76
  input.splice(0, input.length);
76
77
  }
77
-
78
78
  return parsed.help || !!command;
79
- } // Parses input to identify a flag option. Consumes input and adds to parsed output as needed,
79
+ }
80
+
81
+ // Parses input to identify a flag option. Consumes input and adds to parsed output as needed,
80
82
  // returning true if any defined flag was found. If a found flag is missing a required argument, the
81
83
  // next input option will be consumed as that argument. Boolean flags support negation if their
82
84
  // default value is true, integer flags are automatically parsed, and flags that can be provided
83
85
  // multiple times will be concatenated together. Implicit help and version flags are also supported.
84
-
85
-
86
86
  async function maybeParseFlag(input, parsed) {
87
87
  var _value;
88
+ if (!isFlag(input[0])) return;
88
89
 
89
- if (!isFlag(input[0])) return; // end of options and arguments, consume remaining input
90
-
90
+ // end of options and arguments, consume remaining input
91
91
  if (input[0] === '--') {
92
92
  let [, ...argv] = input.splice(0, input.length);
93
93
  parsed.argv.push(...argv);
94
94
  return true;
95
- } // a short flag was provided
96
-
97
-
98
- let short = input[0][1] !== '-' && input[0][1]; // get any value set with `=` and remove any `=` from short values
95
+ }
99
96
 
97
+ // a short flag was provided
98
+ let short = input[0][1] !== '-' && input[0][1];
99
+ // get any value set with `=` and remove any `=` from short values
100
100
  let [name, value] = short ? ['', input[0].substr(2) || null] : input[0].split('=');
101
- value = short && (_value = value) !== null && _value !== void 0 && _value.startsWith('=') ? value.substr(1) : value; // the flag was negated
101
+ value = short && (_value = value) !== null && _value !== void 0 && _value.startsWith('=') ? value.substr(1) : value;
102
102
 
103
+ // the flag was negated
103
104
  let negated = name.startsWith('--no-');
104
- name = name.replace(/^--(no-)?/, ''); // search for the provided short or long flag
105
+ name = name.replace(/^--(no-)?/, '');
105
106
 
107
+ // search for the provided short or long flag
106
108
  let flag = parsed.command.definition.flags.find(f => {
107
109
  return short ? f.short === short : f.name === name;
108
110
  });
109
-
110
111
  if (flag) {
111
112
  // discard current input
112
113
  input.shift();
113
-
114
114
  if (!flag.type || flag.type === 'boolean') {
115
115
  // short boolean flags can be provided with other short flags
116
- if (short && value) input.unshift(`-${value}`); // a value was provided to a boolean flag
116
+ if (short && value) input.unshift(`-${value}`);
117
117
 
118
+ // a value was provided to a boolean flag
118
119
  if (!short && value) {
119
120
  parseError(`Unexpected option argument for '${flagUsage(flag)}'`);
120
- } // a boolean value is based on if it was negated
121
-
121
+ }
122
122
 
123
+ // a boolean value is based on if it was negated
123
124
  value = !negated;
124
125
  } else if (value == null) {
125
126
  // expected a value for a non-boolean flag
126
127
  if (!input.length || isFlag(input[0])) {
127
128
  parseError(`Missing option argument for '${flagUsage(flag)}'`);
128
- } // the value is actually the next input option
129
-
129
+ }
130
130
 
131
+ // the value is actually the next input option
131
132
  value = input.shift();
132
- } // parse the value according to flag properties
133
-
133
+ }
134
134
 
135
+ // parse the value according to flag properties
135
136
  if (flag.type === 'integer') value = parseInt(value, 10);
136
137
  if (flag.parse) value = await flag.parse(value);
137
-
138
138
  if (flag.multiple) {
139
139
  value = (parsed.operators.get(flag) ?? []).concat(value);
140
- } // add this flag to the set of parsed operators
141
-
140
+ }
142
141
 
142
+ // add this flag to the set of parsed operators
143
143
  parsed.operators.set(flag, value);
144
144
  return true;
145
- } // implicit version and help support
146
-
145
+ }
147
146
 
147
+ // implicit version and help support
148
148
  if (parsed.command.definition.version && (name === 'version' || short === 'V')) {
149
149
  parsed.version = true;
150
150
  } else if (name === 'help' || short === 'h') {
151
151
  parsed.help = true;
152
- } // help or version was requested, drain remaining input
153
-
152
+ }
154
153
 
154
+ // help or version was requested, drain remaining input
155
155
  if (parsed.help || parsed.version) {
156
156
  input.splice(0, input.length);
157
157
  return true;
158
158
  }
159
- } // Parses input to identify an argument option. Consumes input and adds to parsed output as needed,
159
+ }
160
+
161
+ // Parses input to identify an argument option. Consumes input and adds to parsed output as needed,
160
162
  // returning true if any defined argument was found. Arguments are matched according to their
161
163
  // respective index and command-line position.
162
-
163
-
164
164
  async function maybeParseArg(input, parsed) {
165
165
  var _parsed$command$defin;
166
+ if (isFlag(input[0])) return;
166
167
 
167
- if (isFlag(input[0])) return; // the argument index needs to be tracked since flag options can precede argument options
168
-
169
- let index = parsed.__args_index__ ?? (parsed.__args_index__ = 0); // there are arguments left to parse
168
+ // the argument index needs to be tracked since flag options can precede argument options
169
+ let index = parsed.__args_index__ ?? (parsed.__args_index__ = 0);
170
170
 
171
+ // there are arguments left to parse
171
172
  if (index < ((_parsed$command$defin = parsed.command.definition.args) === null || _parsed$command$defin === void 0 ? void 0 : _parsed$command$defin.length)) {
172
173
  parsed.__args_index__ += 1;
173
174
  let value = input.shift();
174
175
  let arg = parsed.command.definition.args[index];
175
- if (arg.parse) value = await arg.parse(value); // add this arg to the set of parsed operators
176
+ if (arg.parse) value = await arg.parse(value);
176
177
 
178
+ // add this arg to the set of parsed operators
177
179
  parsed.operators.set(arg, value);
178
180
  return true;
179
181
  }
180
- } // Parses input to identify unknown options. Consumes input and adds to parsed output as needed,
182
+ }
183
+
184
+ // Parses input to identify unknown options. Consumes input and adds to parsed output as needed,
181
185
  // returning true when unknown options are found. By default, unknown options result in an error
182
186
  // unless the command is defined to accept loose options. If `loose` is a string, it will be logged
183
187
  // as a warning when unknown options are found. When the first unknown option is found, all
184
188
  // remaining options are also considered unknown.
185
-
186
-
187
189
  async function maybeParseUnknown(input, parsed) {
188
190
  let {
189
191
  loose
190
192
  } = parsed.command.definition;
191
-
192
193
  if (!loose) {
193
194
  // throw a more descriptive error message
194
195
  let reason = isFlag(input[0]) ? 'Unknown option' : 'Unexpected argument';
@@ -196,43 +197,42 @@ async function maybeParseUnknown(input, parsed) {
196
197
  } else if (typeof loose === 'string') {
197
198
  // warn when loose is a string
198
199
  parsed.log.warn(loose);
199
- } // consume all remaining unknown input options
200
-
200
+ }
201
201
 
202
+ // consume all remaining unknown input options
202
203
  let unknown = input.splice(0, input.length);
203
204
  parsed.argv.push(...unknown);
204
205
  return true;
205
- } // Parses an option's value defined by an environment variable. Adds to parsed output as needed,
206
+ }
207
+
208
+ // Parses an option's value defined by an environment variable. Adds to parsed output as needed,
206
209
  // returning true when the option's value was parsed from the environment. Boolean environment
207
210
  // variables will be interpreted as true unless "false" or "0", and integer environment variables
208
211
  // will be parsed automatically.
209
-
210
-
211
212
  async function maybeParseEnv(option, parsed) {
212
213
  // no env option, already provided, or no defined env var
213
214
  if (!option.env || parsed.operators.has(option) || !(option.env in process.env)) {
214
215
  return;
215
- } // get the value from the process env
216
-
216
+ }
217
217
 
218
+ // get the value from the process env
218
219
  let value = process.env[option.env];
219
220
  let flag = parsed.command.definition.flags.includes(option);
220
-
221
221
  if (flag && (!option.type || option.type === 'boolean')) {
222
222
  // parse booleans as not "false" or not "0"
223
223
  value = value !== 'false' && value !== '0';
224
224
  } else if (option.type === 'integer') {
225
225
  // parse integer option values
226
226
  value = parseInt(value, 10);
227
- } // custom parse and add this option to the set of parsed operators
228
-
227
+ }
229
228
 
229
+ // custom parse and add this option to the set of parsed operators
230
230
  if (option.parse) value = await option.parse(value);
231
231
  parsed.operators.set(option, value);
232
232
  return true;
233
- } // Helper to find and return a matching flag by name.
234
-
233
+ }
235
234
 
235
+ // Helper to find and return a matching flag by name.
236
236
  function findFlag(name, parsed, map) {
237
237
  if (name.startsWith('--')) name = name.substr(2);
238
238
  let {
@@ -240,61 +240,62 @@ function findFlag(name, parsed, map) {
240
240
  } = parsed.command.definition;
241
241
  let match = flags.find(f => f.name === name);
242
242
  return map ? map(match) : match;
243
- } // Throws an error if the provided flag is inclusive of any missing flags.
244
-
243
+ }
245
244
 
245
+ // Throws an error if the provided flag is inclusive of any missing flags.
246
246
  function validateInclusiveFlag(flag, parsed) {
247
247
  if (!flag.inclusive) return;
248
- let invalid = flag.inclusive.reduce((invalid, name) => findFlag(name, parsed, flag => // invalid options are options that have _not_ been provided
248
+ let invalid = flag.inclusive.reduce((invalid, name) => findFlag(name, parsed, flag =>
249
+ // invalid options are options that have _not_ been provided
249
250
  !parsed.operators.has(flag) ? invalid.concat(flag) : invalid), []);
250
-
251
251
  if (invalid.length) {
252
252
  // show full flag usage for invalid options
253
253
  invalid = [flag, ...invalid].map(f => flagUsage(f)).join("', '");
254
254
  parseError(`Options must be used together: '${invalid}'`);
255
255
  }
256
- } // Throws an error if the provided flag is exclusive of other provided flags.
257
-
256
+ }
258
257
 
258
+ // Throws an error if the provided flag is exclusive of other provided flags.
259
259
  function validateExclusiveFlag(flag, parsed) {
260
260
  if (!flag.exclusive) return;
261
- let invalid = flag.exclusive.reduce((invalid, name) => findFlag(name, parsed, flag => // invalid options are options that _have_ been provided
261
+ let invalid = flag.exclusive.reduce((invalid, name) => findFlag(name, parsed, flag =>
262
+ // invalid options are options that _have_ been provided
262
263
  parsed.operators.has(flag) ? invalid.concat(flag) : invalid), []);
263
-
264
264
  if (invalid.length) {
265
265
  // show full flag usage for invalid options
266
266
  invalid = [flag, ...invalid].map(f => flagUsage(f)).join("', '");
267
267
  parseError(`Options cannot be used together: '${invalid}'`);
268
268
  }
269
- } // Returns the attribute name of an option, which may be contingent on it's parsed value. If an
269
+ }
270
+
271
+ // Returns the attribute name of an option, which may be contingent on it's parsed value. If an
270
272
  // option is deprecated and is recommended to be replaced with flag, the attribute name will be
271
273
  // mapped to the replacement flag's attribute name. If a deprecated option is found in the provided
272
274
  // operators, a deprecation warning will be logged with any recommendation message.
273
-
274
-
275
275
  function attributeName(option, parsed) {
276
276
  let {
277
277
  attribute,
278
278
  deprecated
279
279
  } = option;
280
- let value = parsed.operators.get(option) ?? option.default; // the option's attribute name might be contingent on it's value
281
-
282
- let name = typeof attribute === 'function' ? attribute(value) : attribute; // default the attribute name to the camelcase name or short character
283
-
284
- name || (name = option.name ? camelcase(option.name) : option.short); // deprecated options can be mapped to other options
280
+ let value = parsed.operators.get(option) ?? option.default;
281
+ // the option's attribute name might be contingent on it's value
282
+ let name = typeof attribute === 'function' ? attribute(value) : attribute;
283
+ // default the attribute name to the camelcase name or short character
284
+ name || (name = option.name ? camelcase(option.name) : option.short);
285
285
 
286
+ // deprecated options can be mapped to other options
286
287
  if (deprecated) {
287
288
  var _recommend;
289
+ let [until, recommend] = Array.isArray(deprecated) ? deprecated : [typeof deprecated === 'string' ? deprecated : ''];
288
290
 
289
- let [until, recommend] = Array.isArray(deprecated) ? deprecated : [typeof deprecated === 'string' ? deprecated : '']; // recommended replacement flag
290
-
291
+ // recommended replacement flag
291
292
  if ((_recommend = recommend) !== null && _recommend !== void 0 && _recommend.startsWith('--')) {
292
293
  let opt = findFlag(recommend, parsed);
293
294
  recommend = opt ? `Use '${flagUsage(opt)}' instead.` : '';
294
295
  name = opt ? attributeName(opt, parsed) : name;
295
- } // operator was provided, log deprecation warning
296
-
296
+ }
297
297
 
298
+ // operator was provided, log deprecation warning
298
299
  if (parsed.operators.has(option)) {
299
300
  // show better usage message for flags
300
301
  let flag = parsed.command.definition.flags.includes(option);
@@ -302,34 +303,36 @@ function attributeName(option, parsed) {
302
303
  parsed.log.deprecated((`The '${usage}' ${flag ? 'option' : 'argument'} ` + `will be removed in ${until || 'a future release'}. ` + (recommend || '')).trim());
303
304
  }
304
305
  }
305
-
306
306
  return name;
307
- } // Normalizes a command runner into an object of command properties. Optionally derives nested
308
- // commands from any provided definition commands method.
309
-
307
+ }
310
308
 
309
+ // Normalizes a command runner into an object of command properties. Optionally derives nested
310
+ // commands from any provided definition commands method.
311
311
  async function normalizeCommand(command, properties) {
312
312
  var _ref;
313
-
314
313
  // shallow copy command definition
315
- let { ...definition
316
- } = command.definition; // evaluate commands if needed
314
+ let {
315
+ ...definition
316
+ } = command.definition;
317
317
 
318
+ // evaluate commands if needed
318
319
  if (typeof definition.commands === 'function') {
319
320
  definition.commands = await definition.commands();
320
- } // create a shallow copy with additional properties
321
-
321
+ }
322
322
 
323
- let normalized = { ...command,
323
+ // create a shallow copy with additional properties
324
+ let normalized = {
325
+ ...command,
324
326
  ...properties,
325
327
  definition
326
- }; // inherit parent package information by default
328
+ };
327
329
 
330
+ // inherit parent package information by default
328
331
  normalized.packageInformation || (normalized.packageInformation = (_ref = (properties === null || properties === void 0 ? void 0 : properties.parent) || command.parent) === null || _ref === void 0 ? void 0 : _ref.packageInformation);
329
332
  return normalized;
330
- } // Parses and validates command-line arguments according to a command definition.
331
-
333
+ }
332
334
 
335
+ // Parses and validates command-line arguments according to a command definition.
333
336
  export async function parse(command, argv) {
334
337
  // initial input and output
335
338
  let input = argv.slice().filter(a => a != null);
@@ -340,26 +343,26 @@ export async function parse(command, argv) {
340
343
  flags: {},
341
344
  args: {},
342
345
  argv: []
343
- }; // while there is input left, parse it
346
+ };
344
347
 
348
+ // while there is input left, parse it
345
349
  while (input.length) {
346
350
  // parse and mutate input and output, looping if an option is found
347
351
  if (await maybeParseCommand(input, parsed)) continue;
348
352
  if (await maybeParseFlag(input, parsed)) continue;
349
353
  if (await maybeParseArg(input, parsed)) continue;
350
354
  await maybeParseUnknown(input, parsed);
351
- } // help or version requested, skip remaining parse logic
352
-
355
+ }
353
356
 
357
+ // help or version requested, skip remaining parse logic
354
358
  if (parsed.help || parsed.version) {
355
359
  return parsed;
356
- } // validate and map defined flags
357
-
360
+ }
358
361
 
362
+ // validate and map defined flags
359
363
  for (let flag of parsed.command.definition.flags) {
360
364
  // collect last-minute env info
361
365
  await maybeParseEnv(flag, parsed);
362
-
363
366
  if (parsed.operators.has(flag)) {
364
367
  // validate before setting the parsed attribute
365
368
  validateInclusiveFlag(flag, parsed);
@@ -371,20 +374,19 @@ export async function parse(command, argv) {
371
374
  // a default value needs to be set, but not parsed
372
375
  parsed.flags[attributeName(flag, parsed)] = flag.default;
373
376
  }
374
- } // validate and map defined args
375
-
377
+ }
376
378
 
379
+ // validate and map defined args
377
380
  for (let arg of parsed.command.definition.args ?? []) {
378
381
  // collect last-minute env info
379
382
  await maybeParseEnv(arg, parsed);
380
-
381
383
  if (parsed.operators.has(arg)) {
382
384
  var _arg$deprecated, _arg$deprecated$;
383
-
384
385
  // validate before setting the parsed attribute
385
386
  let value = parsed.operators.get(arg);
386
- if (arg.validate) await arg.validate(value, parsed); // a deprecated arg might be mapped to a flag
387
+ if (arg.validate) await arg.validate(value, parsed);
387
388
 
389
+ // a deprecated arg might be mapped to a flag
388
390
  if ((_arg$deprecated = arg.deprecated) !== null && _arg$deprecated !== void 0 && (_arg$deprecated$ = _arg$deprecated[1]) !== null && _arg$deprecated$ !== void 0 && _arg$deprecated$.startsWith('--') && findFlag(arg.deprecated[1], parsed)) {
389
391
  parsed.flags[attributeName(arg, parsed)] = value;
390
392
  } else {
@@ -398,7 +400,6 @@ export async function parse(command, argv) {
398
400
  parsed.args[attributeName(arg, parsed)] = arg.default;
399
401
  }
400
402
  }
401
-
402
403
  return parsed;
403
404
  }
404
405
  export default parse;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/cli-command",
3
- "version": "1.11.0",
3
+ "version": "1.13.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,9 +35,9 @@
35
35
  "test:coverage": "yarn test --coverage"
36
36
  },
37
37
  "dependencies": {
38
- "@percy/config": "1.11.0",
39
- "@percy/core": "1.11.0",
40
- "@percy/logger": "1.11.0"
38
+ "@percy/config": "1.13.0",
39
+ "@percy/core": "1.13.0",
40
+ "@percy/logger": "1.13.0"
41
41
  },
42
- "gitHead": "0a5043cd677266390889063924f342af9b347055"
42
+ "gitHead": "d2e812d14aa446fa580ffa75144a6280627b5a27"
43
43
  }