/
CommandPreparer.ts
690 lines (626 loc) · 30.6 KB
/
CommandPreparer.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
/*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright Contributors to the Zowe Project.
*
*/
import { Constants } from "../../constants/src/Constants";
import { ICommandDefinitionPassOn, ICommandDefinitionPassOnIgnore } from "./doc/ICommandDefinitionPassOn";
import { ImperativeError } from "../../error/src/ImperativeError";
import { ICommandDefinition } from "./doc/ICommandDefinition";
import { ProfileUtils } from "../../profiles/src/utils/ProfileUtils";
import { TextUtils } from "../../utilities/src/TextUtils";
import { OptionConstants } from "./constants/OptionConstants";
import * as DeepMerge from "deepmerge";
import { ICommandProfileTypeConfiguration } from "./doc/profiles/definition/ICommandProfileTypeConfiguration";
import { ICommandOptionDefinition } from "./doc/option/ICommandOptionDefinition";
/**
* Command preparer provides static utilities to ensure that command definitions are suitable for Imperative definition.
*/
export class CommandPreparer {
/**
* Prepare the command definition and apply any pass on traits to children.
* After a definition has been prepared, it should be considered final.
* @param {ICommandDefinition} original - The original command definition tree to "prepare"
* @param {ICommandProfileTypeConfiguration} baseProfile - An optional base profile to add to command definitions
* @return {ICommandDefinition} A copy of the original that has been prepared
*/
public static prepare(original: ICommandDefinition, baseProfile?: ICommandProfileTypeConfiguration): ICommandDefinition {
/**
* Ensure it was specified
*/
if (original == null) {
throw new ImperativeError({ msg: `The command definition document must not be null/undefined.` });
}
/**
* Even before basic validation, ensure that we can stringify and create a copy of the original document.
* The document MUST be capable of this (i.e. every value must be convertable to a canonical JSON format and
* no circular references are allowable)
*/
let copy: ICommandDefinition;
try {
copy = JSON.parse(JSON.stringify(original));
} catch (e) {
throw new ImperativeError({ msg: `An error occurred copying the original command document: ${e.message}` });
}
/**
* Set the default values for optional fields (if they were omitted)
*/
CommandPreparer.setDefaultValues(copy);
/**
* For nodes that wish to "pass on" attributes, ensure that the attribute value to pass on is populated from
* the parent (if omitted).
*/
CommandPreparer.populatePassOnValueFromParent(copy);
/**
* Add global options that should be passed down. We cannot use `appendAutoOptions` because `passOn`
* logic is done before these function is called.
*/
CommandPreparer.appendPassOnOptions(copy);
/**
* Pass on/down any attributes/traits from parents to children (as required)
*/
CommandPreparer.passOn(copy);
/**
* Perform basic validation on the document to ensure that all the necessary fields are present.
*/
CommandPreparer.validateDefinitionTree(copy);
/**
* Prepare the definition by populating with default values, applying global options, etc.
*/
const baseProfileOptions: ICommandOptionDefinition[] = [];
if (baseProfile != null) {
for (const propName of Object.keys(baseProfile.schema.properties)) {
if (baseProfile.schema.properties[propName].optionDefinition != null) {
baseProfileOptions.push(baseProfile.schema.properties[propName].optionDefinition);
}
if (baseProfile.schema.properties[propName].optionDefinitions != null) {
baseProfileOptions.push(...baseProfile.schema.properties[propName].optionDefinitions);
}
}
}
const prepared: ICommandDefinition = CommandPreparer.appendAutoOptions(copy, baseProfileOptions);
/**
* The document prepared for Imperative CLI usage/definition. This should be considered the final document.
*/
return prepared;
}
/**
* Perform preliminary (or post-preparation) basic validation of the command definition tree. Checks to ensure
* that absoultely necessary fields are populated and invalid combos are not present.
* @param {ICommandDefinition} definitionTree - full tree of command definitions to validate
*/
public static validateDefinitionTree(definitionTree: ICommandDefinition) {
CommandPreparer.perfomBasicValidation(definitionTree, []);
/**
* TODO: Advanced Validation
* TODO: Consider protecting against re-used/overridden aliases, command collisions, etc.
* TODO: Consider adding CLI implementation specific command structure validation
*/
}
/**
* Perform preliminary (or post prepared) basic validation of the command definition tree. Checks to ensure
* that absoultely necessary fields are populated and invalid combos are not present.
*
* Note: The root command is a bit of a special case, and is immune to some validation - we can't have a
* name associated because that would cause an extra segment added in yargs.
*
* @param {ICommandDefinition} definition - The current command definition in the tree
* @param {ICommandDefinition[]} definitions - the current set of definitions we've traversed - for diagnostics
*/
private static perfomBasicValidation(definition: ICommandDefinition, definitions: ICommandDefinition[]) {
const definitionDetails: string = "The definition in error has been placed in the additional details field of this error object.";
// Do a quick check for required properties. If none are present, assume that either the definition
// is completely incorrect OR the user did NOT export the definition in a module glob
const props: string[] = Object.keys(definition);
if (props.indexOf("name") < 0 && props.indexOf("description") < 0 && props.indexOf("type") < 0) {
throw new ImperativeError({
msg: `The command definition node being validated does NOT contain any of the required fields (name, description, type). ` +
`Either the definition supplied is completely incorrect (see ICommandDefinition interface for required fields) ` +
`OR you did NOT export the definition in your command definition module (found via the command definition glob). ` +
`Keys/properties present on the definition: ${props.join(",")}. ${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// All nodes must have a non-blank name
if (!(definition as any).isRoot && (definition.name == null || definition.name.trim().length === 0)) {
throw new ImperativeError({
msg: `A command definition node contains an undefined or empty name. ${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// A node cannot have both chained handlers and a single handler
if (definition.handler != null && definition.chainedHandlers != null && definition.chainedHandlers.length > 0) {
throw new ImperativeError({
msg: `A command definition node (${definition.name}) contains both a handler and chained handler ` +
`configuration. The two are mutually exclusive. ${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// verify chained handler configurations are correct
if (definition.chainedHandlers != null) {
for (let chainedHandlerIndex = 0;
chainedHandlerIndex < definition.chainedHandlers.length;
chainedHandlerIndex++) {
const chainedHandler = definition.chainedHandlers[chainedHandlerIndex];
const mappings = chainedHandler.mapping == null ? [] : chainedHandler.mapping;
for (const mapping of mappings) {
if (mapping.to == null) {
throw new ImperativeError({
msg: "Property to argument mapping is invalid for chained handler: " + chainedHandler.handler,
additionalDetails: "Argument mapping does not have a 'to' field. Unable to determine " +
"where to place the arguments for this chained handler."
});
}
if (mapping.from != null && mapping.value != null) {
throw new ImperativeError({
msg: "Property to argument mapping is invalid for chained handler: " + chainedHandler.handler,
additionalDetails: "Argument mapping has both a 'from' field and a 'value' field. " +
"These two fields are mutually exclusive."
});
}
const indicesAhead = mapping.applyToHandlers == null ? [1] : mapping.applyToHandlers;
// make sure they don't try to specify a handler out of bounds of the array
for (const indexAhead of indicesAhead) {
if (chainedHandlerIndex + indexAhead >= definition.chainedHandlers.length) {
throw new ImperativeError({
msg: "Property to argument mapping is invalid for chained handler: " + chainedHandler.handler,
additionalDetails:
TextUtils.formatMessage("The mapping refers to a relative index %s that when added to its " +
"absolute index (%s) is greater than the total number of handlers (%s).",
indexAhead, chainedHandlerIndex, definition.chainedHandlers.length)
});
}
}
}
}
}
// All nodes must have a type
if (definition.type == null || definition.type.trim().length === 0) {
throw new ImperativeError({
msg: `A command definition node (${definition.name}) contains an undefined or empty type. ${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// All nodes must have a description
if (!(definition as any).isRoot &&
(definition.description == null || definition.description.trim().length === 0)) {
throw new ImperativeError({
msg: `A command definition node (${definition.name} of type ${definition.type}) contains an ` +
`undefined or empty description. ${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// Options, if specified, must be an array
if (definition.options != null) {
if (!Array.isArray(definition.options)) {
throw new ImperativeError({
msg: `A command definition node (${definition.name} of type ${definition.type}) options are invalid (not an array). ` +
`${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// If options are specified, perform validation
CommandPreparer.performBasicOptionValidation(definition);
}
// Check positional arguments are an array
if (definition.positionals != null) {
if (!Array.isArray(definition.positionals)) {
throw new ImperativeError({
msg: `A command definition node (${definition.name} of type ${definition.type}) positionals are invalid (not an array). ` +
`${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// If positionals are specified, perform validation
CommandPreparer.performBasicPositionalValidation(definition);
}
// Children must be an array
if (definition.children != null && !Array.isArray(definition.children)) {
throw new ImperativeError({
msg: `A command definition node (${definition.name} of type ${definition.type}) contains ill-formed children. ${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// A group must have children
if (definition.type === "group" && (definition.children == null || definition.children.length === 0)) {
throw new ImperativeError({
msg: `A "group" command definition node (${definition.name}) contains no children. A group implies children. ${definitionDetails}`,
additionalDetails: JSON.stringify(definition)
});
}
// Perform validation for each child
if (definition.children != null) {
for (const child of definition.children) {
CommandPreparer.perfomBasicValidation(child, definitions.concat(definition));
}
}
}
/**
* Perform basic positional operand validation. Ensure that the positional operands are valid and well formed.
* @private
* @static
* @param {ICommandDefinition} definition - The command definition containing positionals to be validated
* @memberof CommandPreparer
*/
private static performBasicPositionalValidation(definition: ICommandDefinition) {
for (const pos of definition.positionals) {
/**
* All positionals must have a name
*/
if (pos.name == null || pos.name.trim().length === 0) {
throw new ImperativeError({
msg: `A positional definition contains an undefined or empty name.`,
additionalDetails: "POSITIONAL_DEFINITION:\n" + JSON.stringify(pos) + "\nCOMMAND_DEFINITION:\n" + JSON.stringify(definition)
});
}
/**
* All positionals must have a type
*/
if (pos.type == null || pos.type.trim().length === 0) {
throw new ImperativeError({
msg: `A positional definition (${pos.name}) contains an undefined or empty type.`,
additionalDetails: "POSITIONAL_DEFINITION:\n" + JSON.stringify(pos) + "\nCOMMAND_DEFINITION:\n" + JSON.stringify(definition)
});
}
/**
* All positionals must have a non-blank description
*/
if (pos.description == null || pos.description.trim().length === 0) {
throw new ImperativeError({
msg: `A positional definition (${pos.name} of type ${pos.type}) contains an ` +
`undefined or empty description.`,
additionalDetails: "POSITIONAL_DEFINITION:\n" + JSON.stringify(pos) + "\nCOMMAND_DEFINITION:\n" + JSON.stringify(definition)
});
}
}
}
/**
* Perform basic option operand validation. Ensure that the option operands are valid and well formed.
* @private
* @static
* @param {ICommandDefinition} definition - The command definition containing options to be validated
* @memberof CommandPreparer
*/
private static performBasicOptionValidation(definition: ICommandDefinition) {
for (const opt of definition.options) {
if (opt == null) {
throw new ImperativeError({
msg: `An option definition is null or undefined.`,
additionalDetails: `COMMAND_DEFINITION:\n${JSON.stringify(definition)}`
});
}
/**
* All options must have a name
*/
if (opt.name == null || opt.name.trim().length === 0) {
throw new ImperativeError({
msg: `An option definition contains an undefined or empty name.`,
additionalDetails: "OPTION_DEFINITION:\n" + JSON.stringify(opt) + "\nCOMMAND_DEFINITION:\n" + JSON.stringify(definition)
});
}
/**
* All options must have a type
*/
if (opt.type == null || opt.type.trim().length === 0) {
throw new ImperativeError({
msg: `An option definition (${opt.name}) contains an undefined or empty type.`,
additionalDetails: "OPTION_DEFINITION:\n" + JSON.stringify(opt) + "\nCOMMAND_DEFINITION:\n" + JSON.stringify(definition)
});
}
/**
* All options must have a non-blank description
*/
if (opt.description == null || opt.description.trim().length === 0) {
throw new ImperativeError({
msg: `An option definition (${opt.name} of type ${opt.type}) contains an ` +
`undefined or empty description.`,
additionalDetails: "OPTION_DEFINITION:\n" + JSON.stringify(opt) + "\nCOMMAND_DEFINITION:\n" + JSON.stringify(definition)
});
}
}
}
/**
* If optional fields have not been populated in the original definition, ensure they are set to the appropriate defaults.
* @private
* @static
* @param {ICommandDefinition} definition - the definition tree to set the default values
* @memberof CommandPreparer
*/
private static setDefaultValues(definition: ICommandDefinition) {
// make sure any array types are at least initialized to empty
definition.options = definition.options || [];
definition.aliases = definition.aliases || [];
definition.positionals = definition.positionals || [];
definition.passOn = definition.passOn || [];
if (definition.children != null) {
for (const child of definition.children) {
CommandPreparer.setDefaultValues(child);
}
}
}
/**
* If the "passOn" specification does not indicate a value, we will extract the value/trait from the parent and
* populate the "passOn" value. This allows parents to pass on their own properties/traits.
* @private
* @static
* @param {ICommandDefinition} definition - the full definition tree
* @memberof CommandPreparer
*/
private static populatePassOnValueFromParent(definition: ICommandDefinition) {
/**
* If the pass on trait has no value, it is taken from the node in which it is defined (meaning
* we are passing-on the trait as-is from the parent).
*/
for (const trait of definition.passOn) {
if (trait.value == null) {
const traitPropertyDef = definition[trait.property];
if (traitPropertyDef == null) {
throw new ImperativeError({
msg: `You cannot pass on a trait (${trait.property}) with a value of ` +
`undefined (current command definition name: ${definition.name} of type ${definition.type}).`
});
}
trait.value = JSON.parse(JSON.stringify(traitPropertyDef));
if (trait.value == null) {
throw new ImperativeError({
msg: `You cannot pass on a trait (${trait.property}) with a value of ` +
`undefined (current command definition name: ${definition.name} of type ${definition.type}).`
});
}
}
}
/**
* Perform for every child
*/
if (definition.children != null) {
for (const child of definition.children) {
CommandPreparer.setDefaultValues(child);
}
}
}
/**
* Appends items which should be passed on to later nodes
* @param definition - The original command definition tree to "prepare"
*/
private static appendPassOnOptions(definition: ICommandDefinition) {
// all groups have --help-examples
definition.passOn.push({
property: "options",
value: {
name: Constants.HELP_EXAMPLES,
group: Constants.GLOBAL_GROUP,
description: "Display examples for all the commands in a group",
type: "boolean"
},
ignoreNodes: [
{
type: "command",
}
],
merge: true
});
// add show-inputs-only to all "command" nodes
definition.passOn.push({
property: "options",
value: {
name: "show-inputs-only",
group: Constants.GLOBAL_GROUP,
description: "Show command inputs and do not run the command",
type: "boolean",
},
ignoreNodes: [
{
type: "group",
}
],
merge: true
});
}
/**
* Appends options (for profiles, global options like help, etc.) automatically
* @param {ICommandDefinition} definition - The original command definition tree to "prepare"
* @param {ICommandOptionDefinition[]} baseProfileOptions - Option definitions sourced from base profile
* @return {ICommandDefinition} A copy of the original that has been prepared
*/
private static appendAutoOptions(definition: ICommandDefinition, baseProfileOptions: ICommandOptionDefinition[]): ICommandDefinition {
// add the json option for all commands
definition.options.push({
name: Constants.JSON_OPTION,
aliases: [Constants.JSON_OPTION_ALIAS],
group: Constants.GLOBAL_GROUP,
description: "Produce JSON formatted data from a command",
type: "boolean"
});
// all commands and groups have --help
definition.options.push({
name: Constants.HELP_OPTION,
aliases: [Constants.HELP_OPTION_ALIAS],
group: Constants.GLOBAL_GROUP,
description: "Display help text",
type: "boolean"
});
// all commands and groups have --help-web
definition.options.push({
name: Constants.HELP_WEB_OPTION,
aliases: [Constants.HELP_WEB_OPTION_ALIAS],
group: Constants.GLOBAL_GROUP,
description: "Display HTML help in browser",
type: "boolean"
});
/**
* Append any profile related options
*/
if (definition.profile != null) {
let types: string[] = [];
if (definition.profile.required) {
types = types.concat(definition.profile.required);
}
if (definition.profile.optional) {
types = types.concat(definition.profile.optional);
}
const profileOptions: string[] = types.filter((type) => (
!Array.isArray(definition.profile.suppressOptions) ?
true : definition.profile.suppressOptions.indexOf(type) < 0
));
profileOptions.forEach((profOpt) => {
const [profOptName, profOptAlias] = ProfileUtils.getProfileOptionAndAlias(profOpt);
definition.options.push({
name: profOptName,
aliases: [profOptAlias],
group: "Profile Options",
description: `The name of a (${profOpt}) profile to load for this command execution.`,
type: "string"
});
});
// Add any option definitions from base profile that are missing in service profile
if (definition.options != null && baseProfileOptions.length > 0 && types.length > 1) {
const optionNames: string[] = definition.options.map((cmdOpt) => cmdOpt.name);
for (const profOpt of baseProfileOptions) {
if (optionNames.indexOf(profOpt.name) === -1) {
definition.options.push(profOpt);
}
}
}
}
if (definition.children != null) {
let allChildrenAreExperimental: boolean = true;
for (const child of definition.children) {
if (!child.experimental) {
allChildrenAreExperimental = false;
break;
}
}
// hide any groups/actions where all the children are experimental but
// the parent isn't explicitly marked experimental
if (allChildrenAreExperimental) {
definition.experimental = true;
}
}
definition.children = definition.children ?
definition.children.map((child) => {
if (definition.experimental) {
// propagate the experimental setting downwards if a parent is experimental
child.experimental = true;
}
// prepare each child
return CommandPreparer.appendAutoOptions(child, baseProfileOptions);
}) : [];
if (definition.enableStdin) {
definition.options.push({
name: Constants.STDIN_OPTION,
aliases: [Constants.STDIN_OPTION_ALIAS],
type: "boolean",
description: definition.stdinOptionDescription || Constants.STDIN_DEFAULT_DESCRIPTION
});
}
definition.options = definition.options.map((option) => {
if (option.group == null) {
if (option.required) {
option.group = "Required Options";
} else {
option.group = "Options";
}
}
if (option.aliases == null) {
option.aliases = [];
}
return option;
});
// Append the format options if requested
if (definition.outputFormatOptions) {
definition.options.push(OptionConstants.RESPONSE_FORMAT_FILTER_OPTION);
definition.options.push(OptionConstants.RESPONSE_FORMAT_OPTION);
definition.options.push(OptionConstants.RESPONSE_FORMAT_HEADER_OPTION);
}
return definition;
}
/**
* A command definition node can indicate any arbitrary field be "passed on" to it's children. The intention is
* to provide convienence for the coder of definition document, when they want to apply the same attributes (such
* as reading from stdin OR which profiles are required) to all of its decedents.
* @param {ICommandDefinition} definition - the original command document
* @param {ICommandDefinition} inherit - the current set of attributes/fields being "passed on" - if a "pass on"
* specification is found in a child document, it overwrites the parents (takes precedence)
* @return {ICommandDefinition} A copy of the original with all "passed on" fields.
*/
private static passOn(definition: ICommandDefinition, inherit?: ICommandDefinitionPassOn[]) {
/**
* Apply the attributes to the current node - assuming the conditions are met
*/
if (inherit != null) {
/**
* Ensure this passOn specification wants this node to inherit the field
*/
for (const trait of inherit) {
if (!CommandPreparer.ignoreNode(definition, trait.ignoreNodes)) {
/**
* Either merge/append or overwrite the field in the definition.
*/
const cloned = (trait.value != null) ?
JSON.parse(JSON.stringify(trait.value)) : undefined;
if (cloned == null) {
throw new ImperativeError({
msg: `The trait (${trait.property}) to pass on cannot have a ` +
`value of undefined. (Current definition name: ${definition.name} ` +
`of type: ${definition.type})`
});
}
if (trait.merge && Array.isArray(definition[trait.property])) {
definition[trait.property] = definition[trait.property].concat(cloned);
} else if (trait.merge && definition[trait.property] != null) {
definition[trait.property] = DeepMerge(definition[trait.property], cloned);
} else {
definition[trait.property] = cloned;
}
}
}
} else {
inherit = [];
}
/**
* traits a cumulative - so we can pass down multiple from the same ancestor OR they may accumulate from
* any number of ancestors.
*/
inherit = definition.passOn.concat(inherit);
if (definition.children != null) {
for (const child of definition.children) {
CommandPreparer.passOn(child, inherit);
}
}
}
/**
* Check if the current node should be ignored. The name of the node is checked agaisnt the specification in
* the pass on parameters.
* @param {ICommandDefinition} node - The command definition node
* @param {ICommandDefinitionPassOnIgnore[]} ignore - The names to ignore
* @returns {boolean} - True if we are to ignore passing on attributes to the passed definition node.
*/
private static ignoreNode(node: ICommandDefinition, ignore: ICommandDefinitionPassOnIgnore[]): boolean {
if (ignore == null) {
return false;
}
for (const ig of ignore) {
if (ig.name != null && ig.type != null) {
if (ig.name === node.name && ig.type === node.type) {
return true;
}
}
if (ig.type == null) {
if (ig.name != null && ig.name === node.name) {
return true;
}
}
if (ig.name == null) {
if (ig.type != null && ig.type === node.type) {
return true;
}
}
}
return false;
}
}