UNPKG

19.1 kBJavaScriptView Raw
1"use strict";
2/*
3 * Copyright (c) 2018, salesforce.com, inc.
4 * All rights reserved.
5 * SPDX-License-Identifier: BSD-3-Clause
6 * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7 */
8var __rest = (this && this.__rest) || function (s, e) {
9 var t = {};
10 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
11 t[p] = s[p];
12 if (s != null && typeof Object.getOwnPropertySymbols === "function")
13 for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
14 t[p[i]] = s[p[i]];
15 return t;
16};
17Object.defineProperty(exports, "__esModule", { value: true });
18const command_1 = require("@oclif/command");
19const core_1 = require("@salesforce/core");
20const kit_1 = require("@salesforce/kit");
21const ts_types_1 = require("@salesforce/ts-types");
22const url_1 = require("url");
23core_1.Messages.importMessagesDirectory(__dirname);
24const messages = core_1.Messages.loadMessages('@salesforce/command', 'flags');
25function merge(kind, flag, describable) {
26 if (ts_types_1.has(flag, 'validate') && ts_types_1.hasFunction(flag, 'parse')) {
27 const parse = flag.parse;
28 flag.parse = (val, ctx) => {
29 validateValue(toValidatorFn(flag.validate)(val), val, kind);
30 return parse(val, ctx);
31 };
32 }
33 return Object.assign({ kind }, flag, { description: describable.description, longDescription: describable.longDescription });
34}
35function option(kind, options, parse) {
36 return merge(kind, command_1.flags.build(Object.assign(options, { parse }))(), options);
37}
38// oclif
39function buildBoolean(options) {
40 return merge('boolean', command_1.flags.boolean(options), options);
41}
42function buildEnum(options) {
43 return Object.assign({ kind: 'enum', type: 'option' }, command_1.flags.enum(options), { options: options.options, description: options.description, longDescription: options.longDescription });
44}
45function buildHelp(options) {
46 const flag = command_1.flags.help(options);
47 return merge('help', command_1.flags.help(options), {
48 description: ts_types_1.ensure(flag.description)
49 });
50}
51function buildInteger(options) {
52 const kind = 'integer';
53 return option(kind, options, (val) => {
54 const parsed = kit_1.toNumber(val);
55 validateValue(Number.isInteger(parsed), val, kind);
56 return validateBounds(kind, parsed, options, (t) => t);
57 });
58}
59function buildOption(options) {
60 return merge('option', command_1.flags.option(options), options);
61}
62function buildString(options) {
63 return merge('string', command_1.flags.string(options), options);
64}
65function buildVersion(options) {
66 const flag = command_1.flags.version(options);
67 return merge('version', flag, {
68 description: ts_types_1.ensure(flag.description)
69 });
70}
71function buildArray(options) {
72 const kind = 'array';
73 return 'map' in options ? buildMappedArray(kind, options) : buildStringArray(kind, options);
74}
75function buildStringArray(kind, options) {
76 const { options: values, validate } = options, rest = __rest(options, ["options", "validate"]);
77 const allowed = new Set(values);
78 return option(kind, rest, val => {
79 const vals = val.split(options.delimiter || ',');
80 validateArrayValues(kind, val, vals, options.validate);
81 validateArrayOptions(kind, val, vals, allowed);
82 return vals;
83 });
84}
85function buildMappedArray(kind, options) {
86 const { options: values, validate } = options, rest = __rest(options, ["options", "validate"]);
87 const allowed = new Set(values);
88 return option(kind, rest, (val) => {
89 const vals = val.split(options.delimiter || ',');
90 validateArrayValues(kind, val, vals, options.validate);
91 const mappedVals = vals.map(options.map);
92 validateArrayOptions(kind, val, mappedVals, allowed);
93 return mappedVals;
94 });
95}
96function buildDate(options) {
97 const kind = 'date';
98 return option(kind, options, (val) => {
99 const parsed = Date.parse(val);
100 validateValue(!isNaN(parsed), val, kind, ` ${messages.getMessage('FormattingMessageDate')}`);
101 return new Date(parsed);
102 });
103}
104function buildDatetime(options) {
105 const kind = 'datetime';
106 return option(kind, options, (val) => {
107 const parsed = Date.parse(val);
108 validateValue(!isNaN(parsed), val, kind, ` ${messages.getMessage('FormattingMessageDate')}`);
109 return new Date(parsed);
110 });
111}
112function buildDirectory(options) {
113 return option('directory', options, (val) => {
114 return validateValue(core_1.sfdc.validatePathDoesNotContainInvalidChars(val), val, 'directory');
115 });
116}
117function buildEmail(options) {
118 return option('email', options, (val) => {
119 return validateValue(core_1.sfdc.validateEmail(val), val, 'email');
120 });
121}
122function buildFilepath(options) {
123 return option('filepath', options, (val) => {
124 return validateValue(core_1.sfdc.validatePathDoesNotContainInvalidChars(val), val, 'filepath');
125 });
126}
127function buildId(options) {
128 return option('id', options, (val) => {
129 return validateValue(core_1.sfdc.validateSalesforceId(val), val, 'id', ` ${messages.getMessage('FormattingMessageId')}`);
130 });
131}
132function buildMilliseconds(options) {
133 const kind = 'milliseconds';
134 return option(kind, options, (val) => {
135 const parsed = kit_1.toNumber(val);
136 validateValue(Number.isInteger(parsed), val, kind);
137 return kit_1.Duration.milliseconds(validateBounds(kind, parsed, options, v => (ts_types_1.isNumber(v) ? v : v[kind])));
138 });
139}
140function buildMinutes(options) {
141 const kind = 'minutes';
142 return option(kind, options, (val) => {
143 const parsed = kit_1.toNumber(val);
144 validateValue(Number.isInteger(parsed), val, kind);
145 return kit_1.Duration.minutes(validateBounds(kind, parsed, options, v => (ts_types_1.isNumber(v) ? v : v[kind])));
146 });
147}
148function buildNumber(options) {
149 const kind = 'number';
150 return option(kind, options, (val) => {
151 const parsed = kit_1.toNumber(val);
152 validateValue(isFinite(parsed), val, kind);
153 return validateBounds(kind, parsed, options, (t) => t);
154 });
155}
156function buildSeconds(options) {
157 const kind = 'seconds';
158 return option(kind, options, (val) => {
159 const parsed = kit_1.toNumber(val);
160 validateValue(Number.isInteger(parsed), val, kind);
161 return kit_1.Duration.seconds(validateBounds(kind, parsed, options, v => (ts_types_1.isNumber(v) ? v : v[kind])));
162 });
163}
164function buildUrl(options) {
165 return option('url', options, (val) => {
166 try {
167 return new url_1.URL(val);
168 }
169 catch (err) {
170 const correct = ` ${messages.getMessage('FormattingMessageUrl')}`;
171 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'InvalidFlagTypeError', [val, 'url', correct || '']);
172 }
173 });
174}
175function buildBuiltin(options = {}) {
176 return Object.assign({}, options, { type: 'builtin' });
177}
178exports.flags = {
179 // oclif
180 /**
181 * A flag type whose presence indicates a `true` boolean value. Produces false when not present.
182 */
183 boolean: buildBoolean,
184 /**
185 * A flag type with a fixed enumeration of possible option values. Produces a validated string from the `options` list.
186 */
187 enum: buildEnum,
188 /**
189 * A flag type useful for overriding the short `char` trigger for emitting CLI help. Emits help and exits the CLI.
190 */
191 help: buildHelp,
192 /**
193 * A flag type that accepts basic integer values. For floats, binary, octal, and hex, see {@link flags.number}.
194 * Produces an integer `number`.
195 */
196 integer: buildInteger,
197 /**
198 * A flag type for custom string processing. Accepts a `parse` function that converts a `string` value to a type `T`.
199 * Produces a type `T`.
200 */
201 option: buildOption,
202 /**
203 * A flag type for returning a raw `string` value without further preprocessing. Produces a string.
204 */
205 string: buildString,
206 /**
207 * A flag type for emitting CLI version information. Emits the CLI version and exits the CLI.
208 */
209 version: buildVersion,
210 // sfdx
211 /**
212 * A flag type for a delimited list of strings with the delimiter defaulting to `,`, e.g., "one,two,three". Accepts
213 * an optional `delimiter` `string` and/or a custom `map` function for converting parsed `string` values into
214 * a type `T`. Produces a parsed (and possibly mapped) array of type `T` where `T` defaults to `string` if no
215 * custom `map` function was provided.
216 */
217 array: buildArray,
218 /**
219 * A flag type for a valid date, e.g., "01-02-2000" or "01/02/2000 01:02:34". Produces a parsed `Date`.
220 */
221 date: buildDate,
222 /**
223 * A flag type for a valid datetime, e.g., "01-02-2000" or "01/02/2000 01:02:34". Produces a parsed `Date`.
224 */
225 datetime: buildDatetime,
226 /**
227 * A flag type for valid directory paths. Produces a validated string.
228 *
229 * **See** [@salesforce/core#sfdc.validatePathDoesNotContainInvalidChars](https://forcedotcom.github.io/sfdx-core/globals.html#sfdc), e.g. "this/is/my/path".
230 */
231 directory: buildDirectory,
232 /**
233 * A flag type for valid email addresses. Produces a validated string.
234 *
235 * **See** [@salesforce/core#sfdc.validateEmail](https://forcedotcom.github.io/sfdx-core/globals.html#sfdc), e.g., "me@my.org".
236 */
237 email: buildEmail,
238 /**
239 * A flag type for valid file paths. Produces a validated string.
240 *
241 * **See** [@salesforce/core#sfdc.validatePathDoesNotContainInvalidChars](https://forcedotcom.github.io/sfdx-core/globals.html#sfdc), e.g. "this/is/my/path".
242 */
243 filepath: buildFilepath,
244 /**
245 * A flag type for valid Salesforce IDs. Produces a validated string.
246 *
247 * **See** [@salesforce/core#sfdc.validateSalesforceId](https://forcedotcom.github.io/sfdx-core/globals.html#sfdc), e.g., "00Dxxxxxxxxxxxx".
248 */
249 id: buildId,
250 /**
251 * A flag type for a valid `Duration` in milliseconds, e.g., "5000".
252 */
253 milliseconds: buildMilliseconds,
254 /**
255 * A flag type for a valid `Duration` in minutes, e.g., "2".
256 */
257 minutes: buildMinutes,
258 /**
259 * A flag type for valid integer or floating point number, e.g., "42". Additionally supports binary, octal, and hex
260 * notation. Produces a parsed `number`.
261 */
262 number: buildNumber,
263 /**
264 * A flag type for a valid `Duration` in seconds, e.g., "5".
265 */
266 seconds: buildSeconds,
267 /**
268 * A flag type for a valid url, e.g., "http://www.salesforce.com". Produces a parsed `URL` instance.
269 */
270 url: buildUrl,
271 // builtins
272 /**
273 * Declares a flag definition to be one of the builtin types, for automatic configuration.
274 */
275 builtin: buildBuiltin
276};
277exports.requiredBuiltinFlags = {
278 json() {
279 return exports.flags.boolean({
280 description: messages.getMessage('jsonFlagDescription'),
281 longDescription: messages.getMessage('jsonFlagLongDescription')
282 });
283 },
284 loglevel() {
285 return exports.flags.enum({
286 options: core_1.Logger.LEVEL_NAMES.concat(core_1.Logger.LEVEL_NAMES.map(l => l.toUpperCase())),
287 default: core_1.LoggerLevel[core_1.Logger.DEFAULT_LEVEL].toLowerCase(),
288 required: false,
289 description: messages.getMessage('loglevelFlagDescription'),
290 longDescription: messages.getMessage('loglevelFlagLongDescription'),
291 parse: (val) => {
292 val = val.toLowerCase();
293 if (core_1.Logger.LEVEL_NAMES.includes(val))
294 return val;
295 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'InvalidLoggerLevelError', [val]);
296 }
297 });
298 }
299};
300exports.optionalBuiltinFlags = {
301 apiversion(opts) {
302 return Object.assign(opts || {}, exports.flags.string({
303 description: resolve(opts, 'description', messages.getMessage('apiversionFlagDescription')),
304 longDescription: resolve(opts, 'longDescription', messages.getMessage('apiversionFlagLongDescription')),
305 parse: (val) => {
306 if (core_1.sfdc.validateApiVersion(val))
307 return val;
308 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'InvalidApiVersionError', [val]);
309 }
310 }));
311 },
312 concise(opts) {
313 return Object.assign(opts || {}, exports.flags.boolean({
314 description: resolve(opts, 'description', messages.getMessage('conciseFlagDescription')),
315 longDescription: resolve(opts, 'longDescription', messages.getMessage('conciseFlagLongDescription'))
316 }));
317 },
318 quiet(opts) {
319 return Object.assign(opts || {}, exports.flags.boolean({
320 description: resolve(opts, 'description', messages.getMessage('quietFlagDescription')),
321 longDescription: resolve(opts, 'longDescription', messages.getMessage('quietFlagLongDescription'))
322 }));
323 },
324 targetdevhubusername(opts) {
325 return Object.assign(opts || {}, exports.flags.string({
326 char: 'v',
327 description: resolve(opts, 'description', messages.getMessage('targetdevhubusernameFlagDescription')),
328 longDescription: resolve(opts, 'longDescription', messages.getMessage('targetdevhubusernameFlagLongDescription'))
329 }));
330 },
331 targetusername(opts) {
332 return Object.assign(opts || {}, exports.flags.string({
333 char: 'u',
334 description: resolve(opts, 'description', messages.getMessage('targetusernameFlagDescription')),
335 longDescription: resolve(opts, 'longDescription', messages.getMessage('targetusernameFlagLongDescription'))
336 }));
337 },
338 verbose(opts) {
339 return Object.assign(opts || {}, exports.flags.boolean({
340 description: resolve(opts, 'description', messages.getMessage('verboseFlagDescription')),
341 longDescription: resolve(opts, 'longDescription', messages.getMessage('verboseFlagLongDescription'))
342 }));
343 }
344};
345function resolve(opts, key, def) {
346 return ts_types_1.hasString(opts, key) ? opts[key] : def;
347}
348function validateValue(isValid, value, kind, correct) {
349 if (isValid)
350 return value;
351 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'InvalidFlagTypeError', [value, kind, correct || '']);
352}
353function validateBounds(kind, value, bounds, extract) {
354 if (bounds.min != null && value < extract(bounds.min)) {
355 throw new core_1.SfdxError(`Expected ${kind} greater than or equal to ${extract(bounds.min)} but received ${value}`, 'InvalidFlagNumericBoundsError');
356 }
357 if (bounds.max != null && value > extract(bounds.max)) {
358 throw new core_1.SfdxError(`Expected ${kind} less than or equal to ${extract(bounds.max)} but received ${value}`, 'InvalidFlagNumericBoundsError');
359 }
360 return value;
361}
362function toValidatorFn(validator) {
363 return (val) => {
364 if (ts_types_1.isString(validator))
365 return new RegExp(validator).test(val);
366 if (ts_types_1.isInstance(validator, RegExp))
367 return validator.test(val);
368 if (ts_types_1.isFunction(validator))
369 return !!validator(val);
370 return true;
371 };
372}
373function validateArrayValues(kind, raw, vals, validator) {
374 validateValue(vals.every(toValidatorFn(validator)), raw, kind, ` ${messages.getMessage('FormattingMessageArrayValue')}`);
375}
376function validateArrayOptions(kind, raw, vals, allowed) {
377 validateValue(allowed.size === 0 || vals.every(t => allowed.has(t)), raw, kind, ` ${messages.getMessage('FormattingMessageArrayOption', [Array.from(allowed).toString()])}`);
378}
379/**
380 * Validate the custom flag configuration. This includes:
381 *
382 * 1. The flag name is in all lowercase.
383 * 2. A string description is provided.
384 * 3. If a char attribute is provided, it is one alphabetical character in length.
385 * 4. If a long description is provided, it is a string.
386 *
387 * @param {SfdxFlagDefinition} flag The flag configuration.
388 * @param {string} key The flag name.
389 * @throws SfdxError If the criteria is not meet.
390 */
391function validateCustomFlag(key, flag) {
392 if (!/^(?!(?:[-]|[0-9]*$))[a-z0-9-]+$/.test(key)) {
393 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'InvalidFlagName', [key]);
394 }
395 if (flag.char && (flag.char.length !== 1 || !/[a-zA-Z]/.test(flag.char))) {
396 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'InvalidFlagChar', [key]);
397 }
398 if (!flag.description || !ts_types_1.isString(flag.description)) {
399 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'MissingOrInvalidFlagDescription', [key]);
400 }
401 if (flag.longDescription !== undefined && !ts_types_1.isString(flag.longDescription)) {
402 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'InvalidLongDescriptionFormat', [key]);
403 }
404 return flag;
405}
406/**
407 * Builds flags for a command given a configuration object. Supports the following use cases:
408 * 1. Enabling common SFDX flags. E.g., { verbose: true }
409 * 4. Defining typed flags. E.g., { myFlag: Flags.array({ char: '-a' }) }
410 * 4. Defining custom typed flags. E.g., { myFlag: Flags.custom({ parse: (val) => parseInt(val, 10) }) }
411 *
412 * @param {FlagsConfig} flagsConfig The configuration object for a flag. @see {@link FlagsConfig}
413 * @param options Extra configuration options.
414 * @returns {flags.Output} The flags for the command.
415 * @ignore
416 */
417function buildSfdxFlags(flagsConfig, options
418// tslint:disable-next-line:no-any matches oclif
419) {
420 const output = {};
421 // Required flag options for all SFDX commands
422 output.json = exports.requiredBuiltinFlags.json();
423 output.loglevel = exports.requiredBuiltinFlags.loglevel();
424 if (options.targetdevhubusername)
425 output.targetdevhubusername = exports.optionalBuiltinFlags.targetdevhubusername();
426 if (options.targetusername)
427 output.targetusername = exports.optionalBuiltinFlags.targetusername();
428 if (options.targetdevhubusername || options.targetusername)
429 output.apiversion = exports.optionalBuiltinFlags.apiversion();
430 // Process configuration for custom and builtin flags
431 ts_types_1.definiteEntriesOf(flagsConfig).forEach(([key, flag]) => {
432 if (isBuiltin(flag)) {
433 if (!ts_types_1.isKeyOf(exports.optionalBuiltinFlags, key)) {
434 throw core_1.SfdxError.create('@salesforce/command', 'flags', 'UnknownBuiltinFlagType', [key]);
435 }
436 output[key] = exports.optionalBuiltinFlags[key](flag);
437 }
438 else {
439 output[key] = validateCustomFlag(key, flag);
440 }
441 });
442 return output;
443}
444exports.buildSfdxFlags = buildSfdxFlags;
445function isBuiltin(flag) {
446 return ts_types_1.hasString(flag, 'type') && flag.type === 'builtin';
447}
448//# sourceMappingURL=sfdxFlags.js.map
\No newline at end of file