@percy/cli-command 1.0.0-beta.70 → 1.0.0-beta.74

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/README.md CHANGED
@@ -1,44 +1,193 @@
1
1
  # @percy/cli-command
2
2
 
3
- The base command class that Percy CLI commands should extend from. Adds commonly used configuration
4
- options to Percy config files, a `#percyrc()` method for parsing config files, and other shared
5
- methods. Also provides common CLI flags along with oclif flag functions.
3
+ Percy CLI command parser and runner.
6
4
 
7
5
  - [Usage](#usage)
8
- - [Mapping CLI Flags](#mapping-cli-flags)
6
+ - [Options](#options)
7
+ - [Args](#args)
8
+ - [Flags](#flags)
9
+ - [Percy](#percy)
9
10
 
10
11
  ## Usage
11
12
 
12
- ```js
13
- import PercyCommand, { flags } from '@percy/cli-command'
14
-
15
- export class PercyPlugin extends PercyCommand {
16
- static flags = {
17
- ...flags.logging,
18
- ...flags.discovery,
19
- ...flags.config
20
- }
21
-
22
- run() {
23
- let { args, flags } = this
24
- let { snapshot, discovery } = this.percyrc()
25
- // ...
26
- }
27
-
28
- finally() {
29
- // called after #run() and also on process termination events
30
- }
31
- }
13
+ The `command` function accepts a name, definition, and callback and returns a runner function that
14
+ accepts an array of command-line arguments.
15
+
16
+ ``` js
17
+ const { command } = require('@percy/cli-command');
18
+
19
+ // example command runner
20
+ const example = command('example', {
21
+ description: 'Example command description',
22
+ // positional arguments
23
+ args: [
24
+ { name: 'one' }
25
+ ],
26
+ // flags and options
27
+ flags: [
28
+ { name: 'foo' }, // boolean by default
29
+ { name: 'bar', type: 'string' }
30
+ ],
31
+ // shows in help output
32
+ examples: [
33
+ // $0 will be replaced by the command name
34
+ '$0 --foo --bar baz qux'
35
+ ]
36
+ }, ({ flags, args }) => {
37
+ // see below for other available properties
38
+ console.log({ flags, args });
39
+ });
40
+
41
+ // remove the script name from argv
42
+ example(process.argv.slice(2));
32
43
  ```
33
44
 
34
- ## Mapping CLI Flags
45
+ ``` shell
46
+ $ node ./example --foo --bar baz qux
47
+ { flags: { foo: true, bar: 'baz' }, args: { one: 'qux' } }
48
+ ```
49
+
50
+ ## Options
51
+
52
+ Only a command `name` is required. However, without any other options, a command will only show the
53
+ default help output with global flags. A command's `callback` will be called when the resulting
54
+ command runner is invoked with an array of command-line arguments. How those arguments are
55
+ interpreted and parsed is defined by the command's `definition`.
56
+
57
+ - `name` — The command name shown in help output, also used to match nested commands.
58
+ - `definition` — A command definition object defines various options to control help output, the
59
+ arguments parser, and other percy options.
60
+ - `description` — The command description shown in help output.
61
+ - `usage` — Custom command usage shown in help output.
62
+ - `examples` — An array of example strings to show in help output.
63
+ - `version` — A version string to display via a `-V, --version` flag.
64
+ - `commands` — An array or function returning an array of nested subcommands.
65
+ - `flags` — Optional flag arguments accepted by the command ([see below](#flags)).
66
+ - `args` — Positional arguments accepted by the command ([see below](#args)).
67
+ - `percy` — Enable creation of a core percy instance ([see below](#percy)).
68
+ - `config` — Automatic percy config registration ([see below](#percy)).
69
+ - `loose` — Push unknown arguments to `argv`. A string value will log as a warning when unknown
70
+ arguments are first encountered by the parser.
71
+ - `exitOnError` — Call `process.exit()` if the command encounters an error while running.
72
+ - `callback` — The command callback invoked after the runner is called with command-line arguments
73
+ and after those arguments are interpreted by the internal parser.
74
+
75
+ The `callback` argument can be a function, an async function, or an async generator function. Given
76
+ an async generator function, the command becomes cancelable via process interruption and is able to
77
+ clean up after itself using traditional try-catch error handling.
78
+
79
+ The `callback` function is invoked with a single argument containing various contextual properties
80
+ according to the command definition:
81
+
82
+ - `flags` — An object containing key-value pairs of flag arguments interpreted by the parser as
83
+ determined by the flag's respective definition ([see below](#flags)).
84
+ - `args` — An object containing key-value pairs of positional arguments interpreted by the parser as
85
+ determined by the arg's respective definition ([see below](#args)).
86
+ - `argv` — An array of unknown arguments that were not parsed due to either the `loose` option or
87
+ the presence of an end-of-arguments delimiter (`--`).
88
+ - `percy` — An instance of `@percy/core` only present when a corresponding `percy` definition option
89
+ is provided ([see below](#percy)).
90
+ - `log` — A `@percy/logger` group namespaced under the command's `name`. The initial loglevel is
91
+ optionally determined by various global logging flags (`--verbose`, `--silent`, `--quiet`).
92
+ - `exit` — Utility function to stop the execution of a command from within the runner. Accepts
93
+ an exit code (default `1`) and an exit reason (default `'EEXIT: ${exitCode}'`). The function will
94
+ throw an error which can be handled, or let bubble where the presence of `exitOnError` will cause
95
+ the process to exit with the provided exit code.
96
+
97
+ ### Flags
98
+
99
+ Each flag definition must contain either a `name` or a `short` option. By default, all flags will be
100
+ interpreted as boolean flags and will produce an error if a value is provided. Setting the `type`
101
+ option to anything other than `'boolean'` will allow providing command-line values and error if one
102
+ _is not_ provided.
103
+
104
+ - `name` — The flag name (i.e. `--flag-name`).
105
+ - `short` — The flag short character (i.e. `-s`).
106
+ - `type` — The flag type as displayed in help output.
107
+ - `default` — The flag's default value when not provided.
108
+ - `parse` — A function used to parse any provided flag value.
109
+ - `validate` — A function that should throw an error when the provided flag value is invalid.
110
+ - `attribute` — A string or function returning a string to set the value at on the `callback`
111
+ argument's `flags` property. Defaults to the camelcase `name` or `short` character.
112
+ - `percyrc` — A string to map this flag to percy config options ([see below](#percy)).
113
+ - `deprecated` — A boolean, string, or tuple used to warn about a deprecated flag and optionally map
114
+ it to an alternate flag ([see below](#deprecations)).
115
+
116
+ ### Args
117
+
118
+ Positional args correspond to their definition index, so the arg definition at index `0` will match
119
+ the first provided command-line argument that is not a flag or subcommand. By default, positional
120
+ args are optional, but can be configured to be required or have default values.
35
121
 
36
- The `#percyrc()` method will parse command flags for Percy configuration mappings as determined by a
37
- `percyrc` flag configuration option.
122
+ - `name` — The arg name shown in help output.
123
+ - `required` — Indicate that this flag is required, which causes an error when not provided.
124
+ - `default` — The arg's default value when not provided.
125
+ - `parse` — A function used to parse the provided argument.
126
+ - `validate` — A function that should throw an error when the provided argument is invalid.
127
+ - `attribute` — A string or function returning a string to set the value at on the `callback`
128
+ argument's `args` property. Defaults to camelcase `name`.
129
+ - `percyrc` — A string to map this arg to percy config options ([see below](#percy)).
130
+ - `deprecated` — A boolean, string, or tuple used to warn about a deprecated arg and optionally map
131
+ it to a flag ([see below](#deprecations)).
132
+
133
+ #### Deprecations
134
+
135
+ When the `deprecated` option is `true` and the flag or positional arg has been provided by
136
+ command-line arguments, a deprecation warning will be logged. A string value indicates the version
137
+ in which this argument will be removed and is reflected in the deprecation message.
138
+
139
+ <details>
140
+ <summary>Remapping deprecated arguments</summary><br>
141
+
142
+ When providing a tuple, the version is the first option of the tuple, while the second option can be
143
+ an alternate flag to use, or a recommendation message to display when the deprecation warning is
144
+ logged. Given an alternate flag, the value will be automatically mapped to the corresponding flag's
145
+ attribute name on the `callback` argument's `flags` property.
38
146
 
39
147
  ``` js
40
- 'allowed-hostnames': flags.string({
41
- // maps the --allowed-hostnames flag to the corresponding config option
42
- percyrc: 'discovery.allowedHostnames'
43
- })
148
+ { name: 'foo', deprecated: true }
149
+ // [percy] Warning: The '--foo' option will be removed in a future release.
150
+
151
+ { name: 'foo', deprecated: 'v2.0.0' }
152
+ // [percy] Warning: The '--foo' option will be removed in v2.0.0.
153
+
154
+ { name: 'foo', deprecated: ['v2.0.0', '--bar'] }
155
+ // [percy] Warning: The '--foo' option will be removed in v2.0.0. Use '--bar' instead.
156
+
157
+ { name: 'foo', deprecated: ['v2.0.0', 'Use the config file option instead.'] }
158
+ // [percy] Warning: The '--foo' option will be removed in v2.0.0. Use the config file option instead.
44
159
  ```
160
+ </details>
161
+
162
+ ### Percy
163
+
164
+ The `percy` definition option accepts an object consisting of `@percy/core` options. The presence of
165
+ this option will add shared percy flags to accepted command-line arguments and provide the command
166
+ callback with a percy instance initialized with the provided options.
167
+
168
+ When the `percy` definition option is `true`, shared percy flags will not be accepted, but a default
169
+ percy instance will still be provided to the command callback when run. Regardless of the `percy`
170
+ option, if the environment variable `PERCY_ENABLE` is `0`, the callback _will not_ receive a percy
171
+ instance (and can act accordingly).
172
+
173
+ - `percy` — Enables creation of a `@percy/core` instance initialized with provided options.
174
+ - `discoveryFlags` — When `false` prevents percy discovery flags from being accepted, but will
175
+ still allow other percy flags such as `--config` and `--dry-run`.
176
+ - `...` — All other options are passed along to the new percy instance.
177
+
178
+ #### Config
179
+
180
+ Acceptable percy config file options can be extended before the file is loaded via
181
+ `PercyConfig.addSchema()` and `PercyConfig.addMigration()`. These functions can be called before the
182
+ command is run to achieve the same effect as including them in the command definition. However, the
183
+ presence of a `percy` definition option will also signal `@percy/core` config schemas and migrations
184
+ to be automatically registered before the following options are registered.
185
+
186
+ - `config` — Percy config schemas and migrations to register while creating the command runner.
187
+ - `schemas` — An array of percy config schemas to automatically register.
188
+ - `migrations` — An array of percy config migrations to automatically register.
189
+
190
+ Additionally, flags and positional arguments that define a `percyrc` option will have their
191
+ associated values mapped to a corresponding `percy` property that is used when initializing a
192
+ `@percy/core` instance. Provided options are validated against config schemas and are made available
193
+ on the command callback argument's `percy.config` property.
package/bin/readme ADDED
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const { formatHelp } = require('@percy/cli-command/dist/help');
6
+
7
+ const CWD = process.cwd();
8
+ const README = path.join(CWD, 'README.md');
9
+
10
+ const HELP_USAGE = /^(.*)?(usage:.*)$/gis;
11
+ const CMDS_TMPL = /(<!-- commands -->)(.*)(<!-- commandsstop -->)/is;
12
+
13
+ async function generateReadmeCommands(pkg) {
14
+ if (!pkg['@percy/cli']?.commands) return '';
15
+
16
+ let sections = [];
17
+ let toc = [];
18
+
19
+ for (let cmdPath of pkg['@percy/cli'].commands) {
20
+ let { default: command } = require(path.join(CWD, cmdPath));
21
+ command = { ...command, parent: { name: 'percy' } };
22
+
23
+ for (let cmd of [command, ...(command.definition.commands || [])]) {
24
+ if (!cmd.callback) continue;
25
+ if (cmd !== command) cmd = { ...cmd, parent: command };
26
+ if (cmd.parent.parent) cmd.name = `${cmd.parent.name}:${cmd.name}`;
27
+
28
+ let title = `\`percy ${cmd.name}\``;
29
+ let slug = title.slice(1, -1).replace(/\s/g, '-').replace(/:/g, '');
30
+ toc.push(`* [${title}](#${slug})`);
31
+
32
+ let help = await formatHelp(cmd);
33
+ help = help.replace(HELP_USAGE, '$1```\n$2```');
34
+ sections.push(`### ${title}\n\n${help}\n`);
35
+ }
36
+ }
37
+
38
+ return [
39
+ toc.join('\n'),
40
+ sections.join('\n')
41
+ ].join('\n\n');
42
+ }
43
+
44
+ async function updatePackageReadme() {
45
+ let pkg = require(path.join(CWD, 'package.json'));
46
+ let content = fs.readFileSync(README, 'utf8')
47
+ .replace(CMDS_TMPL, `$1\n${await generateReadmeCommands(pkg)}$3`)
48
+ fs.writeFileSync(README, content);
49
+ }
50
+
51
+ updatePackageReadme();
package/dist/command.js CHANGED
@@ -3,111 +3,238 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
+ exports.command = command;
6
7
  exports.default = void 0;
7
8
 
8
- var _command = _interopRequireDefault(require("@oclif/command"));
9
+ var _logger = _interopRequireDefault(require("@percy/logger"));
9
10
 
10
11
  var _config = _interopRequireDefault(require("@percy/config"));
11
12
 
12
13
  var _utils = require("@percy/config/dist/utils");
13
14
 
14
- var _logger = _interopRequireDefault(require("@percy/logger"));
15
+ var CoreConfig = _interopRequireWildcard(require("@percy/core/dist/config"));
16
+
17
+ var builtInFlags = _interopRequireWildcard(require("./flags"));
18
+
19
+ var _help = _interopRequireDefault(require("./help"));
20
+
21
+ var _parse = _interopRequireDefault(require("./parse"));
15
22
 
16
23
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
24
 
18
- function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
25
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
26
+
27
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
28
+
29
+ // Copies a command definition and adds built-in flags and config options.
30
+ function withBuiltIns(definition) {
31
+ var _def$flags, _def$config$schemas, _def$config$migration;
32
+
33
+ let def = { ...definition
34
+ };
35
+ def.flags = [...((_def$flags = def.flags) !== null && _def$flags !== void 0 ? _def$flags : [])]; // ensure built-ins aren't already overridden
36
+
37
+ let addDedupedFlag = flag => {
38
+ if (!def.flags.find(f => f.name === flag.name)) {
39
+ let short = def.flags.find(f => f.short === flag.short);
40
+ def.flags.push(short ? (0, _utils.del)({ ...flag
41
+ }, 'short') : flag);
42
+ }
43
+ }; // add percy specific built-in flags
44
+
19
45
 
20
- // The PercyCommand class that all Percy CLI commands should extend
21
- // from. Provides common #init() and #catch() methods and provides other methods
22
- // for loading configuration and checking if Percy is enabled.
23
- class PercyCommand extends _command.default {
24
- constructor(...args) {
25
- super(...args);
46
+ if (def.percy && def.percy !== true) {
47
+ builtInFlags.PERCY.forEach(addDedupedFlag); // maybe include percy discovery flags
26
48
 
27
- _defineProperty(this, "log", (0, _logger.default)('cli:command'));
49
+ if (def.percy.discoveryFlags !== false) {
50
+ builtInFlags.DISCOVERY.forEach(addDedupedFlag);
51
+ }
52
+ } // always add global built-in flags
53
+
54
+
55
+ builtInFlags.GLOBAL.forEach(addDedupedFlag); // copy any existing config before adding to it
56
+
57
+ def.config = { ...def.config
58
+ };
59
+ def.config.schemas = [...((_def$config$schemas = def.config.schemas) !== null && _def$config$schemas !== void 0 ? _def$config$schemas : [])];
60
+ def.config.migrations = [...((_def$config$migration = def.config.migrations) !== null && _def$config$migration !== void 0 ? _def$config$migration : [])]; // add percy specific built-in config options
61
+
62
+ if (def.percy) {
63
+ def.config.schemas.unshift(...CoreConfig.schemas);
64
+ def.config.migrations.unshift(...CoreConfig.migrations);
28
65
  }
29
66
 
30
- // Initialize flags, args, the loglevel, and attach process handlers to allow
31
- // commands to seemlessly cleanup on interupt or termination
32
- init() {
67
+ return def;
68
+ } // Calls the provided callback and handles callback cancelation when process signals are
69
+ // triggered. If the provided callback returns a generator, it will automatically run and throw
70
+ // an error when canceled to be able to gracefully handle it's own cancelation.
71
+
72
+
73
+ async function handleProcessSignals(callback) {
74
+ let signals = ['SIGUSR1', 'SIGUSR2', 'SIGTERM', 'SIGINT', 'SIGHUP'];
75
+ let signalError; // keep track of signal handlers for cleanup
76
+
77
+ let signalHandlers = signals.map(signal => {
78
+ let handler = () => {
79
+ signalError = Object.assign(new Error(signal), {
80
+ canceled: true,
81
+ exitCode: 0,
82
+ signal
83
+ });
84
+ };
85
+
86
+ process.on(signal, handler);
87
+ return [signal, handler];
88
+ });
89
+
90
+ try {
91
+ // maybe async function
92
+ let gen = await callback(); // run any returned generator
93
+
94
+ if (typeof (gen === null || gen === void 0 ? void 0 : gen.next) === 'function' && (typeof gen[Symbol.iterator] === 'function' || typeof gen[Symbol.asyncIterator] === 'function')) {
95
+ let result = await gen.next();
96
+
97
+ while (!result.done) {
98
+ result = signalError ? await gen.throw(signalError) : await gen.next(result.value);
99
+ }
100
+ }
101
+ } finally {
102
+ // always cleanup
103
+ for (let [signal, handler] of signalHandlers) {
104
+ process.off(signal, handler);
105
+ }
106
+ }
107
+ } // Helper to throw an error with an exit code and optional reason message
108
+
109
+
110
+ function exit(exitCode, reason = '') {
111
+ let err = reason instanceof Error ? reason : new Error(reason);
112
+ throw Object.assign(err, {
113
+ exitCode
114
+ });
115
+ } // Runs the parsed command callback with a contextual argument consisting of specific parsed input
116
+ // and other common command helpers and properties.
117
+
118
+
119
+ async function runCommandWithContext(parsed) {
120
+ let {
121
+ command,
122
+ flags,
123
+ args,
124
+ argv,
125
+ log
126
+ } = parsed; // include flags, args, argv, logger, exit helper, and env info
127
+
128
+ let context = {
129
+ flags,
130
+ args,
131
+ argv,
132
+ log,
133
+ exit
134
+ };
135
+ let env = context.env = process.env;
136
+ let def = command.definition; // automatically include a preconfigured percy instance
137
+
138
+ if (def.percy) {
33
139
  let {
34
- args,
35
- flags
36
- } = this.parse();
37
-
38
- _logger.default.loglevel('info', flags);
39
-
40
- this.flags = flags;
41
- this.args = args; // log and map deprecated flags
42
-
43
- for (let f in this.constructor.flags) {
44
- let {
45
- deprecated
46
- } = this.constructor.flags[f];
47
-
48
- if (deprecated && flags[f] != null) {
49
- if (deprecated === true) deprecated = {};
50
- let {
51
- until: ver,
52
- map,
53
- alt
54
- } = deprecated;
55
- let message = `The --${f} flag ` + [`will be removed in ${ver || 'a future release'}.`, map ? `Use --${map} instead.` : alt || ''].join(' ').trim();
56
- this.log.deprecated(message);
57
- if (map) flags[map] = flags[f];
140
+ Percy
141
+ } = await Promise.resolve().then(() => _interopRequireWildcard(require('@percy/core'))); // set defaults and prune preconfiguraton options
142
+
143
+ let conf = (0, _utils.del)({
144
+ server: false,
145
+ ...def.percy
146
+ }, 'discoveryFlags');
147
+ Object.defineProperty(context, 'percy', {
148
+ configurable: true,
149
+
150
+ get() {
151
+ // percy is disabled, do not return an instance
152
+ if (env.PERCY_ENABLE === '0') return; // redefine the context property once configured
153
+
154
+ Object.defineProperty(context, 'percy', {
155
+ // map and merge percy arguments with config options
156
+ value: new Percy([...parsed.operators.entries()].reduce((conf, [opt, value]) => opt.percyrc ? (0, _utils.set)(conf, opt.percyrc, value) : conf, conf))
157
+ });
158
+ return context.percy;
58
159
  }
59
- } // ensure cleanup is always performed
60
160
 
161
+ });
162
+ } // wrap and bind the parsed command callback
61
163
 
62
- let cleanup = () => this.finally(new Error('SIGTERM'));
63
164
 
64
- process.on('SIGHUP', cleanup);
65
- process.on('SIGINT', cleanup);
66
- process.on('SIGTERM', cleanup);
67
- } // Log errors using the Percy logger
165
+ await handleProcessSignals(command.callback.bind(null, context));
166
+ } // Returns a command runner function that when run will parse provided command-line options and run
167
+ // the parsed command callback. The returned runner will automatically output version and help
168
+ // information when requested, and handle any thrown errors exiting when appropriate.
68
169
 
69
170
 
70
- async catch(err) {
71
- try {
72
- // real errors will bubble
73
- await super.catch(err);
74
- } catch (err) {
75
- // oclif exit method actually throws an error, let it continue
76
- if (err.oclif && err.code === 'EEXIT') throw err; // log all other errors and exit
171
+ function command(name, definition, callback) {
172
+ definition = withBuiltIns(definition); // auto register config schemas and migrations
77
173
 
78
- this.log.error(err);
79
- this.exit(1);
80
- }
81
- } // Returns true or false if Percy has not been disabled
174
+ _config.default.addSchema(definition.config.schemas);
82
175
 
176
+ _config.default.addMigration(definition.config.migrations);
83
177
 
84
- isPercyEnabled() {
85
- return process.env.PERCY_ENABLE !== '0';
86
- } // Parses command flags and maps them to config options according to their
87
- // respective `percyrc` parameter. The flag input is then merged with options
88
- // loaded from a config file and default config options. The PERCY_TOKEN
89
- // environment variable is also included as a convenience.
178
+ async function runner(argv = []) {
179
+ // reset loglevel for testing
180
+ _logger.default.loglevel('info');
90
181
 
182
+ let log = (0, _logger.default)('cli');
91
183
 
92
- percyrc(initialOverrides = {}) {
93
- let overrides = Object.entries(this.constructor.flags).reduce((conf, [name, flag]) => {
94
- if (!flag.percyrc || this.flags[name] == null) return conf;
95
- return (0, _utils.set)(conf, flag.percyrc, this.flags[name]);
96
- }, initialOverrides); // will also validate config and log warnings
184
+ try {
185
+ // parse input
186
+ let parsed = await (0, _parse.default)(runner, argv);
187
+
188
+ if (parsed.version) {
189
+ // version requested
190
+ log.stdout.write(parsed.command.definition.version + '\n');
191
+ } else if (parsed.help || !parsed.command.callback) {
192
+ // command help requested
193
+ log.stdout.write((await (0, _help.default)(parsed.command)) + '\n');
194
+ } else {
195
+ // run command callback
196
+ await runCommandWithContext(parsed);
197
+ }
198
+ } catch (err) {
199
+ // auto log unhandled error messages
200
+ if (err.message && !err.signal) {
201
+ if (err.exitCode === 0) log.warn(err.message);else log.error(err);
202
+ } // exit when appropriate
97
203
 
98
- let config = _config.default.load({
99
- path: this.flags.config,
100
- overrides
101
- }); // set config: false to prevent core from reloading config
102
204
 
205
+ if (err.exitCode !== 0) {
206
+ var _err$exitCode;
103
207
 
104
- return Object.assign(config, {
105
- skipUploads: this.flags.debug,
106
- dryRun: this.flags['dry-run'],
107
- config: false
108
- });
109
- }
208
+ (_err$exitCode = err.exitCode) !== null && _err$exitCode !== void 0 ? _err$exitCode : err.exitCode = 1;
209
+ err.message || (err.message = `EEXIT: ${err.exitCode}`);
110
210
 
211
+ if (definition.exitOnError) {
212
+ process.exit(err.exitCode);
213
+ } // re-throw when not exiting
214
+
215
+
216
+ throw err;
217
+ }
218
+ }
219
+ } // define command meta information
220
+
221
+
222
+ Object.defineProperties(runner, {
223
+ name: {
224
+ enumerable: true,
225
+ value: name
226
+ },
227
+ definition: {
228
+ enumerable: true,
229
+ value: definition
230
+ },
231
+ callback: {
232
+ enumerable: true,
233
+ value: callback
234
+ }
235
+ });
236
+ return runner;
111
237
  }
112
238
 
113
- exports.default = PercyCommand;
239
+ var _default = command;
240
+ exports.default = _default;