ConsoleCommandsParser
class
- Demo
- Minimal Theory
- Creating of Console Line Interface — Stepwise Guidance
The class is intended to be used for parsing the argument vectors of console commands with validation and transforming to specific TypeScript types constrained to an object. Also, the generation of help (reference) text about console commands functionality is available.
Demo
The following example will be explained in detail.
import { RawObjectDataProcessor } from "@yamato-daiwa/es-extensions";
import { ConsoleCommandsParser } from "@yamato-daiwa/es-extensions-nodejs";
namespace ApplicationConsoleLineInterface {
export enum CommandPhrases {
projectBuilding = "build",
packingOfBuild = "pack",
projectDeploying = "deploy",
referenceGenerating = "help"
}
export type SupportedCommandsAndParametersCombinations =
ProjectBuildingConsoleCommand |
PackingOfBuildConsoleCommand |
ProjectDeployingConsoleCommand |
ReferenceGeneratingConsoleCommand;
export type ProjectBuildingConsoleCommand = Readonly<{
phrase: CommandPhrases.projectBuilding;
requiredStringOption: string;
optionalStringOption?: string;
}>;
export type PackingOfBuildConsoleCommand = Readonly<{
phrase: CommandPhrases.packingOfBuild;
enumerationLikeStringOption: "FOO" | "BAR" | "BAZ";
numericOption?: number;
limitedNumericOption?: number;
}>;
export type ProjectDeployingConsoleCommand = Readonly<{
phrase: CommandPhrases.projectDeploying;
booleanOption: boolean;
JSON5_Option?: Readonly<{ foo: string; bar?: number; }>;
}>;
export type ReferenceGeneratingConsoleCommand = Readonly<{
phrase: CommandPhrases.referenceGenerating;
}>;
export const specification: ConsoleCommandsParser.CommandLineInterfaceSpecification = {
applicationName: "Example task manager",
applicationDescription: "Executes various tasks.",
commandPhrases: {
[CommandPhrases.projectBuilding]: {
isDefault: true,
description: "Builds the project for specified mode.",
options: {
requiredStringOption: {
description: "Example required string option",
type: ConsoleCommandsParser.ParametersTypes.string,
required: true,
shortcut: "a"
},
optionalStringOption: {
description: "Example optional string option",
type: ConsoleCommandsParser.ParametersTypes.string,
required: false
}
}
},
[CommandPhrases.packingOfBuild]: {
description: "Create the deployable pack of the project",
options: {
enumerationLikeStringOption: {
description: "Example enumeration like string option",
type: ConsoleCommandsParser.ParametersTypes.string,
defaultValue: "FOO",
allowedAlternatives: [
"FOO",
"BAR",
"BAZ"
]
},
numericOption: {
description: "Example numeric option",
type: ConsoleCommandsParser.ParametersTypes.number,
numbersSet: RawObjectDataProcessor.NumbersSets.naturalNumber,
required: false
},
limitedNumericOption: {
description: "Example numeric option with fixed minimal and maximal value",
type: ConsoleCommandsParser.ParametersTypes.number,
numbersSet: RawObjectDataProcessor.NumbersSets.anyInteger,
required: false,
minimalValue: -10,
maximalValue: 10
}
}
},
[CommandPhrases.projectDeploying]: {
description: "Deploys the project.",
options: {
booleanOption: {
description: "Example boolean option",
type: ConsoleCommandsParser.ParametersTypes.boolean,
shortcut: "b"
},
JSON5_Option: {
description: "Example JSON5 option",
type: ConsoleCommandsParser.ParametersTypes.JSON5,
shortcut: "j",
required: false,
validValueSpecification: {
foo: {
type: RawObjectDataProcessor.ValuesTypesIDs.string,
required: true,
minimalCharactersCount: 1
},
bar: {
type: RawObjectDataProcessor.ValuesTypesIDs.number,
numbersSet: RawObjectDataProcessor.NumbersSets.anyInteger,
required: false,
minimalValue: 1
}
}
}
}
},
[CommandPhrases.referenceGenerating]: {}
}
};
}
Minimal Theory
There are no official terminology and standards concerning the
console command anatomy — just a more or less established convention.
At last, the process.argv
(shorthand for the "arguments vector,"
theconvention inherited from C++ language is an array of strings.
What to do with them — the application author and/or his customers decide.
In fact, the parsing of the console command is the kind of problem of the structured external data parsing where the indexed array of strings is such data, thus the elements order is critical. Node.js does not provide any functionality for the parsing and validation of such arrays, and in addition, not every third-party library specialized on console commands parsing can do it type-safely (as far as possible).
Conventional Terminology
Generally, a console command presents itself as a sequence of string values separated by spaces:
command argument1 argument2 ... argumentN
On the Webpack utility example, such a command could be like:
webpack build --mode development
- Command
- In essence, it is the application name — either full or abbreviated.
On the example of the console interface of the Angular framework, the
ng
in the command. However, usually the command is even with the full name of the utility or close to it, for example,webpack
,gulp
,lerna
. - Option / Option key
- Begins with a double n-dash (like
--mode
in the above example). - Parameter
- The value of the option.
For the above example, the value
development
is the parameter of the--mode
option.
- If the option has no parameter, it is considered a
boolean option with a
true
value. Accordingly, when this option has not been specified, it is equivalent tofalse
for this option. - The option could have an abbreviation consisting of a single n-dash and a letter
(for example,
-d
). Noteworthy, in the Webpack utility, although there is the abbreviated option-m
, it is not the shorthand for the--mode
. As it appears, though the abbreviations are inputted quickly, to memorize them frequent usage is required. - Arguments could include spaces; however, in this case, they must be wrapped in quotes.
--option1=parameter1 --option2=parameter2 ... --optionN=parameterN
also exists.
Currently, such syntax is not being supported by
ConsoleCommandsParser, but if this syntax will be highly demanded,
adding support in future versions is possible.
What is the build
in the above example?
Good question, and it deserves to be considered in a separate section.
Command Phrase
The command phase is the non-option first
argument of the console command, referring to the certain
functionality of the console application.
For example, in yda build --mode DEVELOPMENT
, the
build
is the command phrase, but because it
must not consist of one word, it is being called by phrase.
If the command phrase included multiple words, besides wrapping in quotes, it also possible to use
sentence fused writing methods such as camel case.
The command phrase could be explicit or implicit (the command phrase by default).
For example, in webpack build --mode development
, the
default command phrase build
could be
omitted (webpack --mode development
).
Discriminated Unions in TypeScript
To use the ConsoleCommandsParser, it is required to understand the concept of discriminated unions in TypeScript language. In essence, discriminated unions mean that some type could be one of multiple object subtypes with certain properties set, herewith one of these properties identifies the specific subtype. So, the discriminated union is the generalizing of the multiple specific object types, such as the "passenger car" could be "sedan," "hatchback," "minivan," and others, herewith this set must be finite and there is the "type" column in the car documentation, thanks to which the car type could be uniquely known.
Let us consider it on the example related to ConsoleCommandsParser.
Assume that the console application developed by us has command phrases
build
, pack
,
deploy
, help
.
For now, it is negligible which functionality of the console applications these
command phrases represent, but if for the users the short command phrases are
desired while for the developers the meaningful command phrases are desired, we can store the
short ones to the values of the enumeration:
enum CommandPhrases {
projectBuilding = "build",
packingOfBuild = "pack",
projectDeploying = "deploy",
referenceGenerating = "referenceGenerating"
}
Let us create the object for each command phrase which will
include the phrase
property with one of the
values of the enumeration CommandPhrases
defined above, and also
the options actual for the corresponding command phrases.
Let all command phrases except the last one have
the options.
type ProjectBuildingConsoleCommand = {
phrase: CommandPhrases.projectBuilding;
requiredStringOption: string;
optionalStringOption?: string;
};
type PackingOfBuildConsoleCommand = {
phrase: CommandPhrases.packingOfBuild;
enumerationLikeStringOption: "FOO" | "BAR" | "BAZ";
numericOption?: number;
limitedNumericOption?: number;
};
type ProjectDeployingConsoleCommand = {
phrase: CommandPhrases.projectDeploying;
booleanOption: boolean;
JSON5_Option?: Readonly<{ foo: string; bar?: number; }>;
};
type ReferenceGeneratingConsoleCommand = {
phrase: CommandPhrases.referenceGenerating;
};
We could not know in advance which exactly command phrase will be inputted by user, but with correctly defined validation rules (it will be explained how to) it will be one of the command phrases defined above:
type SupportedCommandsAndParametersCombinations =
ProjectBuildingConsoleCommand |
PackingOfBuildConsoleCommand |
ProjectDeployingConsoleCommand |
ReferenceGeneratingConsoleCommand;
The parse
method of
ConsoleCommandsParser
will return the object of the defined
above SupportedCommandsAndParametersCombinations
with
2 additional properties:
NodeJS_InterpreterAbsolutePath
and
executableFileAbsolutePath
— they are the first
two elements of process.argv
array.
In fact, the ParsedCommand
generic in
ParsedCommand<SupportedCommandsAndParametersCombinations>
tells TypeScript that to one of the subtypes of
SupportedCommandsAndParametersCombinations
union these
properties will be added:
const parsedConsoleCommand: ConsoleCommandsParser.
ParsedCommand<SupportedCommandsAndParametersCombinations> =
ConsoleCommandsParser.parse(ApplicationConsoleLineInterface.specification);
But, how will we know which command phrase exactly has been inputted by the user of the application?
Because all subtypes of
SupportedCommandsAndParametersCombinations
discriminated union
have the identifier-like phrase
property which is
unique for each subtype of this union, using
conditional statements (for this case switch/case
is just right)
we can know the specific subtype:
switch (parsedConsoleCommand.phrase) {
case ApplicationConsoleLineInterface.CommandPhrases.projectBuilding: {
console.log("Build project");
console.log(parsedConsoleCommand.requiredStringOption);
console.log(parsedConsoleCommand.optionalStringOption);
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.packingOfBuild: {
console.log("Pack build");
console.log(parsedConsoleCommand.enumerationLikeStringOption);
console.log(parsedConsoleCommand.numericOption);
console.log(parsedConsoleCommand.limitedNumericOption);
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.projectDeploying: {
console.log("Deploy project")
console.log(parsedConsoleCommand.phrase);
console.log(parsedConsoleCommand.booleanOption);
console.log(parsedConsoleCommand.JSON5_Option);
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.referenceGenerating: {
console.log(ConsoleCommandsParser.generateFullHelpReference(ApplicationConsoleLineInterface.specification));
}
}
As a result, in each case-block we can access the properties of
parsedConsoleCommand
corresponding to the current command phrase, and if we try to access the
property corresponding to another command phrase,
TypeScript will notice it as an error.
Admittedly, there are many optional properties in this example, so a
non-undefined check will be required before using these properties.
Creating of Console Line Interface — Stepwise Guidance
Step 1. Defining of Command Phrases Set
Decide, which command phrases will be available in your application.
- Maybe, you will need only one command phrase. If your application is narrow specialized, it is normal.
- For now, it is better to define only such command phrases which are clear for you. You can add more command phrases later.
Store all your command phrases to the enumeration. The enumeration keys not necessarily must match with the sequences of characters which the user will input to the console: let the keys be longer, but understandable for programmers without documentation. And if you, like the developers of most console applications, want to make the inputted command phrases short, then store them to the values of the enumeration elements. Fortunately, TypeScript allows to define string-type enumerations, which is impossible or limited in many other programming languages.
export enum CommandPhrases {
projectBuilding = "build",
packingOfBuild = "pack",
projectDeploying = "deploy",
referenceGenerating = "help"
}
As you can see, in the example above the compound nouns without verbs are being using if the name begins from verb herewith not belong to function or method, it creates the confusion. However, it is the stylistic requirement, with which you may disagree and set your own.
It is recommended to write this code in a separate file, for example
ApplicationConsoleLineInterface.ts.
Also, for the clear definition of the context, we recommend to wrap all the content of this file by
namespace, for example ApplicationConsoleLineInterface
.
Although some developers are against the usage of namespaces (in TypeScript),
such a negative attitude to namespaces is usually associated with outdated ways of splitting
the code into modules, and such usage, as in our example (contextual container for types and
constants) not carries any problems.
import { RawObjectDataProcessor } from "@yamato-daiwa/es-extensions";
import { ConsoleCommandsParser } from "@yamato-daiwa/es-extensions-nodejs";
namespace ApplicationConsoleLineInterface {
export enum CommandPhrases {
projectBuilding = "build",
packingOfBuild = "pack",
projectDeploying = "deploy",
referenceGenerating = "help"
}
}
Step 2. Determination of Options for Command Phrases
Decide which options you will support for each command phrase, as well as their types, requiredness and default values (if any) of each option.
- Again, to get the feedback from your code faster, for initial phase you can limit yourself to the minimal set of options which are most thoughtful.
- Some command phrases could have no options at all — it is normal,
and if you will feel the need in the future, you can always add them.
In our example, the
referenceGenerating
command phrase has no options.
Now, for each command phrase define the object type with the following properties:
- phrase
- Must contain the element of the commanded phrases
enumeration (
CommandPhrases
in above example) corresponding to the same command phrase as the type you are defining. This property plays the role of the identifier, with which it will be necessary to determine which exactly command phrase the user has inputted. As the type of this property it is required to specify the certain element of enumeration (for example,CommandPhrases.projectBuilding
), not the enumeration itself (from the viewpoint of discriminated unions in TypeScript it is not senselessly). - Command options
All options for the current command phrase. The keys of must not be the same as ones which the user will input to the console, so let their names be clear to the programmers without documentation. The type of each of these properties must be one of the following supported types:
string
number
boolean
- The object constrained to ParsedJSON from the core package (@yamato-daiwa/es-extensions)
If the option is non-required herewith no default value planned, place the [+Term--YDID question mark] before colon (the key and value's type separator).
Finally, declare the union of all the object types defined above
for each command phrase (in the below example, it is the
SupportedCommandsAndParametersCombinations
).
import { RawObjectDataProcessor } from "@yamato-daiwa/es-extensions";
import { ConsoleCommandsParser } from "@yamato-daiwa/es-extensions-nodejs";
namespace ApplicationConsoleLineInterface {
export enum CommandPhrases {
projectBuilding = "build",
packingOfBuild = "pack",
projectDeploying = "deploy",
referenceGenerating = "help"
}
export type SupportedCommandsAndParametersCombinations =
ProjectBuildingConsoleCommand |
PackingOfBuildConsoleCommand |
ProjectDeployingConsoleCommand |
ReferenceGeneratingConsoleCommand;
export type ProjectBuildingConsoleCommand = Readonly<{
phrase: CommandPhrases.projectBuilding;
requiredStringOption: string;
optionalStringOption?: string;
}>;
export type PackingOfBuildConsoleCommand = Readonly<{
phrase: CommandPhrases.packingOfBuild;
enumerationLikeStringOption: "FOO" | "BAR" | "BAZ";
numericOption?: number;
limitedNumericOption?: number;
}>;
export type ProjectDeployingConsoleCommand = Readonly<{
phrase: CommandPhrases.projectDeploying;
booleanOption: boolean;
JSON5_Option?: Readonly<{ foo: string; bar?: number; }>;
}>;
export type ReferenceGeneratingConsoleCommand = Readonly<{
phrase: CommandPhrases.referenceGenerating;
}>;
}
- Again, drawing your attention which stylistic requirements could be concerning the naming of types. The name of each type corresponding to specific command phrase does not including the verbs and ends with ConsoleCommand.
- The usage of Readonly utility type is not required, but recommended, because using it we are explicitly expressing that the properties of the objects types defined by us are not indented to be changed during program execution.
Step 3. Determination of Console Application Specification
Based on the console command specification, the parsing and validation of the inputted console command will be performed, and if necessary — the generation of the help about usage of this console application.
Define the constant of the
ConsoleCommandsParser.CommandLineInterfaceSpecification type —
it is a multi-level object, in which should be contained the specification of
all the command phrases including the options of
each of them, as well as the data for the help generation.
If you have followed the advice to wrap the code in the namespace such as
ApplicationConsoleLineInterface
, then the long name of the
constant such as consoleCommandLineInterfaceSpecification
is not required: we have declared the context
ApplicationConsoleLineInterface, so the constant could be named just
specification
.
import { RawObjectDataProcessor } from "@yamato-daiwa/es-extensions";
import { ConsoleCommandsParser } from "@yamato-daiwa/es-extensions-nodejs";
namespace ApplicationConsoleLineInterface {
export enum CommandPhrases {
projectBuilding = "build",
packingOfBuild = "pack",
projectDeploying = "deploy",
referenceGenerating = "help"
}
export type SupportedCommandsAndParametersCombinations =
ProjectBuildingConsoleCommand |
PackingOfBuildConsoleCommand |
ProjectDeployingConsoleCommand |
ReferenceGeneratingConsoleCommand;
export type ProjectBuildingConsoleCommand = Readonly<{
phrase: CommandPhrases.projectBuilding;
requiredStringOption: string;
optionalStringOption?: string;
}>;
export type PackingOfBuildConsoleCommand = Readonly<{
phrase: CommandPhrases.packingOfBuild;
enumerationLikeStringOption: "FOO" | "BAR" | "BAZ";
numericOption?: number;
limitedNumericOption?: number;
}>;
export type ProjectDeployingConsoleCommand = Readonly<{
phrase: CommandPhrases.projectDeploying;
booleanOption: boolean;
JSON5_Option?: Readonly<{ foo: string; bar?: number; }>;
}>;
export type ReferenceGeneratingConsoleCommand = Readonly<{
phrase: CommandPhrases.referenceGenerating;
}>;
export const specification: ConsoleCommandsParser.CommandLineInterfaceSpecification = {
applicationName: "Example task manager",
applicationDescription: "Executes various tasks.",
commandPhrases: {
[CommandPhrases.projectBuilding]: {
isDefault: true,
description: "Builds the project for specified mode.",
options: {
requiredStringOption: {
description: "Example required string option",
type: ConsoleCommandsParser.ParametersTypes.string,
required: true,
shortcut: "a"
},
optionalStringOption: {
description: "Example optional string option",
type: ConsoleCommandsParser.ParametersTypes.string,
required: false
}
}
},
[CommandPhrases.packingOfBuild]: {
description: "Create the deployable pack of the project",
options: {
enumerationLikeStringOption: {
description: "Example enumeration like string option",
type: ConsoleCommandsParser.ParametersTypes.string,
defaultValue: "FOO",
allowedAlternatives: [
"FOO",
"BAR",
"BAZ"
]
},
numericOption: {
description: "Example numeric option",
type: ConsoleCommandsParser.ParametersTypes.number,
numbersSet: RawObjectDataProcessor.NumbersSets.naturalNumber,
required: false
},
limitedNumericOption: {
description: "Example numeric option with fixed minimal and maximal value",
type: ConsoleCommandsParser.ParametersTypes.number,
numbersSet: RawObjectDataProcessor.NumbersSets.anyInteger,
required: false,
minimalValue: -10,
maximalValue: 10
}
}
},
[CommandPhrases.projectDeploying]: {
description: "Deploys the project.",
options: {
booleanOption: {
description: "Example boolean option",
type: ConsoleCommandsParser.ParametersTypes.boolean,
shortcut: "b"
},
JSON5_Option: {
description: "Example JSON5 option",
type: ConsoleCommandsParser.ParametersTypes.JSON5,
shortcut: "j",
required: false,
validValueSpecification: {
foo: {
type: RawObjectDataProcessor.ValuesTypesIDs.string,
required: true,
minimalCharactersCount: 1
},
bar: {
type: RawObjectDataProcessor.ValuesTypesIDs.number,
numbersSet: RawObjectDataProcessor.NumbersSets.anyInteger,
required: false,
minimalValue: 1
}
}
}
}
},
[CommandPhrases.referenceGenerating]: {}
}
};
}
- Specify the
applicationName
property with the name of your application. If is not mandatory to be even with the inputted to the console application name, and if it consists of multiple words, then it is possible to input them with the space separation. This value will be used in the generation of the help about the usage of the console application. - Unlike
applicationName
, propertyapplicationDescription
is optional, however it is strongly recommended to fill it with a short description so the generated help will be full-fledged. - The property
commandPhrases
must contain the specifications of all the command phrases. This specification is the associative array-like object which keys must match with the command phrases. The safest way to guarantee this matching is refer the keys ofcommandPhrases
to the corresponding elements of the enumerationCommandPhrases
using the bracket notation (for example,[CommandPhrases.packingOfBuild]
). About the values of this associative array,- If corresponding command phrase is the default one,
specify the
true
value toisDefault
boolean property. Because the default command phrase could be only one,isDefault: true
could be specified maximally to one command phrase, otherwise the exception will be thrown once application start. - Similar to explained above
applicationDescription
property, for the command phrases specifications there is thedescription
property, which is optional but strongly recommend to be filled specified for the generating of high quality reference. In the example below, the description has been omitted only forhelp
(CommandPhrases.referenceGenerating
) command phrase. - In the command phrase has options, its' specification must be specified in the options property which is also the associative array-like object.
- If corresponding command phrase is the default one,
specify the
Defining the Command Phrases' Options
A majority of time of current step will be taken by the defining of the options of the command phrases if there are many of them. However, it is nothing difficult: it is required specify the options (as inputted) via object keys, and their specifications such as type, requirement, etc. via object-type values with below properties:
- Specify option type by the element of
ConsoleCommandsParser.ParametersTypes
enumeration. Currently, the string, number, boolean, and objects (must be inputted with JSON5 format) types are supported. - If the option has default value, specify
defaultValue
property with the value of the corresponding type. Otherwise, you have to specify the required property with the boolean value (of course, withtrue
if the option is required). Herewith, nodefaultValue
could be specified (by the TypeScript types definitions) for the optional boolean option because the optional boolean option omitted during command inputting is equivalent to explicitly specifiedfalse
value. - If you are writing the high quality code, but for the inputting speed the names of options are
too short to be understood without documentation, define the
newName
property with meaningful name for the code maintainers. - If you want define the single letter abbreviation for the option, then define
the
shortcut
property. The n-dash, which the user will be required to specify when input the abbreviated option, here, when defining of the specification, could be safely omitted.
All other properties are type-dependent. Let us consider them.
Special Properties of String-type Options
Currently there is only one such property —
allowedAlternatives
.
If you want the string option to be able to accept the values among limited
set, specify them as an array to the allowedAlternatives
property.
Special Properties of Number-type Options
The required property for this type of options is
numbersSet
.
It must be defined with the element of
RawObjectDataProcessor.NumbersSets
enumeration adopted from
the core package of library:
naturalNumber
nonNegativeInteger
negativeInteger
negativeIntegerOrZero
anyInteger
positiveDecimalFraction
negativeDecimalFraction
decimalFractionOfAnySign
anyRealNumber
Optionally the minimalValue
and
maximalValue
properties could be specified.
Special Properties of Object-type Options
Currently, there is only one such property —
validValueSpecification
, but it is the required one.
Using this, it is required to define the validation rules for the object to which the
JSON5 string will be transformed.
This property has type RawObjectDataProcessor.PropertiesSpecification , adopted from the core package. This object-type property is pretty similar to the options specification, but here is the API of RawObjectDataProcessor class is being used.
Step 4. Creating of the Logic of the Console Commands Parsing
The defining of the console command application specification has been finished; now it can be used for the parsing of the inputted console command. To parse the console command, it is required to call the parse static method of ConsoleCommandsParser class:
import { ConsoleCommandsParser } from "@yamato-daiwa/es-extensions-nodejs";
import ApplicationConsoleLineInterface from "./ConsoleLineInterface";
const parsedConsoleCommand: ConsoleCommandsParser.
ParsedCommand<ApplicationConsoleLineInterface.SupportedCommandsAndParametersCombinations> =
ConsoleCommandsParser.parse(ApplicationConsoleLineInterface.specification);
- The console application specification defined at previous step is the required parameter.
- If to omit the second parameter, the arguments vector will
be taken from
process.argv
. - It is required to specify the generic parameter with
SupportedCommandsAndParametersCombinations
defined at the second step.
Now, we need to determine which exactly command phrase was inputted, and also safely
(without any
type errors in TypeScript) access
its options (unless the optional options wihtout default values
will require the check for undefined
before using them).
We can do this with the help of switch/case
statement:
switch (parsedConsoleCommand.phrase) {
case ApplicationConsoleLineInterface.CommandPhrases.projectBuilding: {
console.log("Build project", parsedConsoleCommand);
console.log("requiredStringOption", parsedConsoleCommand.requiredStringOption);
console.log("optionalStringOption", parsedConsoleCommand.optionalStringOption);
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.packingOfBuild: {
console.log("Pack project", parsedConsoleCommand);
console.log("enumerationLikeStringOption", parsedConsoleCommand.enumerationLikeStringOption);
console.log("numericOption", parsedConsoleCommand.numericOption);
console.log("limitedNumericOption", parsedConsoleCommand.limitedNumericOption);
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.projectDeploying: {
console.log("Deploy project", parsedConsoleCommand);
console.log("booleanOption", parsedConsoleCommand.booleanOption);
console.log("JSON5_Option", parsedConsoleCommand.JSON5_Option);
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.referenceGenerating: {
console.log(ConsoleCommandsParser.generateFullHelpReference(ApplicationConsoleLineInterface.specification));
}
}
Step 5. Launching of the Application by Name
Currently, the application already could be launched by the file path with ts-node, for example:
ts-node EntryPoint.test.ts --requiredStringOption test --optionalStringOption sample
Although many of you will not be satisfied by this (because want to launch the application by the command, it means the application name according confusing established terminology), let us consider in short when such variant fits to requirements thus completed previous steps will be enough. First of all this are the cases when the developed console interface planned to use exclusively for single project wihtout publication to npm or by other methods. In particular:
- Server Applications
- Commonly, such application has only one command phrase for the server starting. Here, the HTTP port, logging level etc. are usually been specified via options.
- Automation scripts
- Usually it is the generating, copying or modifying the files, herewith the target is as specific as the general utilities such Gulp does not fit.
Well, but how to make possible the invocation of the utility by its name like gulp, webpack etc.?
First of all, because the Node.js runtime does not support the TypeScript, it is required to provide the transpiling of the source code to JavaScript. It could be done by the console line interface of the TypeScript package, or the utilities as Webpack mentioned above (one ore more plugins specializing of TypeScript will require).
But before run the transpiling, it is required to export from the
entry point the function or class, one of which
methods will launch the application's logic.
"Export from the entry point" is sounds weird, because normally the entry point has many
imports, but exports nothing.
The reason is, in the case of the Node.js console utilities, the
entry point is being executed indirectly, via executable file.
Here is the example of the entry point which exports the
executeApplication
function:
import { ConsoleCommandsParser } from "@yamato-daiwa/es-extensions-nodejs";
import ApplicationConsoleLineInterface from "./ConsoleLineInterface";
export function executeApplication(): void {
const parsedConsoleCommand: ConsoleCommandsParser.
ParsedCommand<ApplicationConsoleLineInterface.SupportedCommandsAndParametersCombinations> =
ConsoleCommandsParser.parse(ApplicationConsoleLineInterface.specification);
switch (parsedConsoleCommand.phrase) {
case ApplicationConsoleLineInterface.CommandPhrases.projectBuilding: {
// ...
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.packingOfBuild: {
// ...
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.projectDeploying: {
// ...
break;
}
case ApplicationConsoleLineInterface.CommandPhrases.referenceGenerating: {
console.log(ConsoleCommandsParser.generateFullHelpReference(ApplicationConsoleLineInterface.specification));
}
}
}
Once transpiling done, create one more JavaScript file (for example, Executable.js) manually with the content like the following one:
#!/usr/bin/env node
require("./EntryPoint").executeApplication();
Here the first line contents the shebang specifying that this file must be executed by
Node.js.
Then, we need to import the function launching the application
(executeApplication
in above example) and call it.
Why not to make this file the entry point? It seems like the JavaScript file except the shebang line, right?
Now, it is required to reference to above executable file from the
package.json file of your project.
To make your utility callable by name, the bin
field
must to be filled with the associative array-like object,
where the keys are the commands, and values are the
relative paths to executable files
(".js" extension could be omitted):
{
// ...
"bin": {
"my_app": "Executable"
},
// ...
}
More details about bin
field you can found in the
npm official documentation.
To install your utility to another projects, it is required to fill certain other fields of
package.json, such as name
and
version
.
More fields need to be filled, if you are going to publish your utility by
npm.
On the npm official website, there is the
brief manual about this.