-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
312 lines (258 loc) · 7.06 KB
/
index.js
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
"use strict";
var fs = require("fs");
var path = require("path");
var lib = require("../lib");
var mode = require("../lib/mode");
var cows = require("../cows");
/**
* Common command line arguments
*
* @typedef {Object} CLIArgs
* @property {boolean} [help] Show help
* @property {boolean} [list] List available cows
* @property {string} message Message
* @package
*/
/**
* Full parsed cow arguments
*
* @typedef {import("../lib").CowAllOptions & CLIArgs} CowArgs
*/
/**
* Argument data
*
* @typedef {Object} ArgData
* @property {string} data Data
* @property {number} next Next argument list index
* @package
*/
/**
* Parsed `package.json` file
*
* @type {Record<string, string | string[] | Record<string, string>>}
* @package
*/
var package_json = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
/**
* Get all available cow mode data IDs
*
* @returns {string[]}
*/
function getModes() {
return mode.modes
.slice(1)
.concat(mode.customModes)
.map(function(mode) { return mode.id; });
}
/**
* Get the argument data for the given position of the argument list
*
* @param {string} token Option token
* @param {number} j Current token index
* @param {string[]} argv Argument list
* @param {number} i Current argument index
* @returns {ArgData} Parsed argument data
* @package
*/
function getArg(token, j, argv, i) {
// Get rest of token
var data = token.slice(j + 1);
var next = i;
// Check next token
if (data.length === 0) {
next += 1;
if (next < argv.length) {
data = argv[next];
}
}
// Return data
return {
data: data,
next: next
};
}
/**
* Print scripts help
*
* @package
*/
function printHelp() {
// Current year
var year = new Date().getFullYear();
// Get current script
var script = process.argv[1].replace(/\\/g, "/");
script = script.slice(script.lastIndexOf("/") + 1).replace(/\./, "");
// Script info
var info =
"moojs, cowsayjs, cowthinkjs v" + package_json.version + "\n" +
"Copyright (c) " + year + " Erick Rincones\n" +
"Licensed under the MIT License\n\n";
// Brief
var brief =
" A nodejs clone of the classic cowsay and cowthink cli commands.\n\n";
// Usage
var usage =
"Usage: " + script + " [options] [message]\n\n";
// Options
var modes = getModes().join("");
var modeOptsPad = Array(modes.length < 12 ? 12 - modes.length : 15).join(" ");
var modeOptsSep = modeOptsPad.length === 15 ? "\n" : " ";
var modeOpts = modes + modeOptsSep + modeOptsPad;
var reflexive = script === "moojs" ?
" -r A reflexive cow will think instead say the message\n" : "";
var options =
"Options:\n" +
" -h Print this message\n" +
" -e EYES Set the first two chars of EYES as the cow eyes\n" +
" -f COW Specify the cow to use\n" +
" -l Show the list of available cows\n" +
" -n No wrap and show original message\n" +
reflexive +
" -T TONGUE Set the first tow characters of TONGUE as the cow tongue\n" +
" -W COLUMN Specifies where the message should be wrapped\n" +
" -" + modeOpts + "Select one mode to use a predefined face\n\n";
// Details
var details =
"By default EYES are \"oo\" and COLUMN is 40. The eyes and tongue\n" +
"cannot be changed for some cows specified by the -f option.\n\n";
// Examples
var examples =
"Examples:\n" +
" cowsayjs Moo world\n" +
" cowthinkjs -f small -b -T \"U \" -W 10 I am a reflexive little cow\n" +
" moojs -f dragon -r Another reflexive cow\n" +
" echo -e 'This message\\n\\nwill\\tnot\\n\\n be wraped' | moojs -n\n\n";
// Extra
var extra =
"Full documentation and source code: https://github.com/erincones/cowsayjs\n" +
"Online version: https://nextmoo.vercel.app\n";
// Print help
var help = info + brief + usage + options + details + examples + extra;
process.stdout.write(help);
}
/**
* Print cows list
*
* @package
*/
function printCorral() {
var corral = cows.corral.concat(cows.customCorral);
var i = 0;
do {
var cow = corral[i];
process.stdout.write(cow.name + "\n");
} while (++i < corral.length);
}
/**
* Parse the script arguments
*
* @param {import("../lib/box").BoxAction} [action] Default cow action
* @returns {CowArgs} Script arguments
*/
function parseArgs(action) {
// Set default action and initial values
/** @type {CowArgs} */
var args = { action: action, message: "" };
var argv = process.argv;
var modes = getModes();
var stop = false;
var i = 2;
// Check number of arguments
if (argv.length <= 2) {
return args;
}
// Parse arguments
do {
var arg = argv[i];
// Handle message
if (arg.length <= 1 || arg[0] !== "-") {
stop = true;
}
// Handle arguments
else {
var token = arg.slice(1);
var j = 0;
do {
// Check each option
var opt = token[j];
switch (opt) {
// Boolean options
case "h": args.help = true; return args;
case "l": args.list = true; break;
case "n": args.wrap = false; break;
case "r": args.action = action || "think"; break;
// Options with arguments
case "e": case "f": case "T": case "W": {
var parsed = getArg(token, j, argv, i);
j = token.length;
i = parsed.next;
switch (opt) {
case "e": args.eyes = parsed.data; break;
case "f": args.cow = parsed.data; break;
case "T": args.tongue = parsed.data; break;
case "W":
if (args.wrap !== false) {
args.wrap = parseInt(parsed.data);
}
}
break;
}
default:
// Mode options
if (modes.indexOf(opt) !== -1) {
args.mode = opt;
}
else {
process.stderr.write("Unknown option: " + opt + "\n");
}
}
} while (++j < token.length);
}
} while (!stop && ++i < argv.length);
// Set message
if (stop && i < argv.length) {
args.message = argv.slice(i).join(" ");
}
// Return arguments
return args;
}
/**
* Execute the given cow aruments
*
* @param {CowArgs} args Cow arguments
*/
function execArgs(args) {
// Print help
if (args.help) {
printHelp();
}
// Print list of cows
else if (args.list) {
printCorral();
}
// Print cow with shell message
else if (process.stdin.isTTY === true || args.message.length !== 0) {
process.stdout.write(lib.moo(args) + "\n");
}
// Print cow with piped message
else {
args.message = "";
process.stdin.on("data", function(chunk) {
// Get data
args.message += chunk.toString();
}).on("end", function() {
// Remove empty final line and print cow
args.message = args.message.replace(/\n$/, "");
process.stdout.write(lib.moo(args) + "\n");
});
}
}
/**
* Argument parse and other cli utilites
*
* @module cowsayjs/cli
*/
module.exports = {
parseArgs: parseArgs,
execArgs: execArgs
};