UNPKG

5.36 kBJavaScriptView Raw
1"use strict";
2
3
4/**
5 * Suppose you have
6 * ```js
7 * var def = {
8 * test: {
9 * desc: "prepare Karma tests.",
10 * opts: {
11 * dir: {
12 * desc: "Karma spec folder. Default is 'spec'.",
13 * args: "spec"
14 * }
15 * }
16 * },
17 * doc: {
18 * desc: "create documentation."
19 * }
20 * };
21 * ```
22 * Then
23 * ```js
24 * parse( "node tfw.js", def ) === {}
25 * ```
26 *
27 * ```js
28 * parse( "node tfw.js doc", def ) === { doc:{} }
29 * ```
30 *
31 * ```js
32 * parse( "node tfw.js test", def ) === { test: { dir: "spec" } }
33 * ```
34 *
35 * ```js
36 * parse( "node tfw.js test -dir jasmin/", def ) === { test: { dir: "jasmine/" } }
37 * ```
38 *
39 *
40 * @param {array} commandLineArgs - Array of strings provided by the
41 * command line. Most of the time, you will use `process.argv` to
42 * populate this.
43 * @param {object} def - Defaults.
44 * @returns {object} The options found on the command line.
45 */
46exports.parse = function ( commandLineArgs, def ) {
47 const options = {};
48 // Remove `node` and the script name.
49 commandLineArgs.shift();
50 commandLineArgs.shift();
51
52 let currentCommand = null;
53
54 while ( commandLineArgs.length > 0 ) {
55 const arg = commandLineArgs.shift();
56 if ( isOption( arg ) ) {
57 // That's an option.
58 // Remove heading dash.
59 const opt = arg.substr( 1 );
60 checkIfCommandHasThisOption( currentCommand, opt, def );
61 options[ currentCommand ][ opt ] = parseOptionArguments( commandLineArgs, currentCommand, opt, def );
62 } else {
63 // That's a command.
64 const cmd = arg;
65 checkIfCommandExists( cmd, def );
66 options[ cmd ] = {};
67 currentCommand = cmd;
68 }
69 }
70
71 applyDefaultValuesForOptionsArguments( options, def );
72 return options;
73};
74
75
76/**
77 * @return Text description of arguments based on `def`.
78 */
79exports.usage = function ( def ) {
80 let output = "Accepted arguments:\n";
81 const
82 commandNames = Object.keys( def ),
83 commandMaxLength = commandNames.reduce( ( acc, val ) => Math.max( acc, val.length ), 0 );
84
85 commandNames.forEach( function ( commandName ) {
86 output += ` ${commandName.yellow.bold}:`;
87 output += spc( 1 + commandMaxLength - commandName.length );
88 const cmd = def[ commandName ];
89 if ( typeof cmd.desc === 'string' ) output += cmd.desc;
90 output += "\n";
91 if ( !cmd.opts ) return;
92 const optionNames = Object.keys( cmd.opts );
93 optionNames.forEach( function ( optName ) {
94 output += ` -${optName.yellow}:`.yellow;
95 output += spc( 1 + commandMaxLength - commandName.length );
96 const opt = cmd.opts[ optName ];
97 if ( typeof opt.desc === 'string' ) output += opt.desc;
98 output += "\n";
99 } );
100
101 } );
102
103 return `${output}\n`;
104};
105
106/**
107 * @param {number} nbSpaces - Number of spaces.
108 * @returns {string} A string containing `nbSpaces` spaces.
109 */
110function spc( nbSpaces ) {
111 let
112 output = "",
113 counter = typeof nbSpaces !== "number" ? nbSpaces : 0;
114 while ( counter-- > 0 ) output += " ";
115 return output;
116}
117
118/**
119 * If the command line is `node prog build -debug -output "www/assets"`, `options` will look like this:
120 * ```
121 * {
122 * build: {
123 * debug: [],
124 * output: ["www/assets"]
125 * }
126 * }
127 * ```
128 *
129 */
130function applyDefaultValuesForOptionsArguments( options, def ) {
131 Object.keys( options ).forEach( function ( cmdName ) {
132 const
133 cmdOpts = options[ cmdName ],
134 defOpts = def[ cmdName ].opts;
135 if ( defOpts ) {
136 Object.keys( defOpts ).forEach( function ( defOptName ) {
137 if ( cmdOpts[ defOptName ] ) return;
138 const defOptValue = defOpts[ defOptName ];
139 if ( countArgs( defOptValue ) > 0 ) {
140 cmdOpts[ defOptName ] = defOptValue.args;
141 }
142 } );
143 }
144 } );
145}
146
147
148function isOption( name ) {
149 return name.charAt( 0 ) === '-';
150}
151
152
153function checkIfCommandExists( cmd, def ) {
154 if ( !def[ cmd ] )
155 throw `Unknown command '${cmd}'!`;
156}
157
158
159function checkIfCommandHasThisOption( cmd, opt, def ) {
160 if ( !cmd )
161 throw "Expected a command, but found '" + opt + "'!";
162 var availableOptions = def[ cmd ].opts;
163 if ( !availableOptions )
164 throw "Command '" + cmd + "' does not accept any option, but you provided '" + opt + "'!";
165 if ( !availableOptions[ opt ] )
166 throw "Command '" + cmd + "' does not accept the option you provided: '" + opt + "'!";
167}
168
169
170function parseOptionArguments( commandLineArgs, currentCommand, opt, def ) {
171 var args = [];
172 var count = countArgs( def[ currentCommand ].opts[ opt ] );
173 while ( count-- > 0 ) {
174 if ( commandLineArgs.length === 0 )
175 throw `Missing mandatory argument for option '${opt}' of command '${currentCommand}'!`;
176 args.push( commandLineArgs.shift() );
177 }
178 return args;
179}
180
181
182function countArgs( opt ) {
183 if ( !opt.args ) return 0;
184 if ( !Array.isArray( opt.args ) ) opt.args = [ opt.args ];
185 return opt.args.length;
186}
\No newline at end of file