breadc 0.6.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +16 -15
- package/dist/index.cjs +589 -459
- package/dist/index.d.ts +144 -132
- package/dist/index.mjs +589 -449
- package/package.json +11 -17
package/dist/index.cjs
CHANGED
|
@@ -1,25 +1,142 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
const kolorist = require('kolorist');
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
const color = require('@breadc/color');
|
|
7
6
|
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
for (const
|
|
13
|
-
|
|
7
|
+
function makePluginContainer(plugins = []) {
|
|
8
|
+
const onPreCommand = {};
|
|
9
|
+
const onPostCommand = {};
|
|
10
|
+
for (const plugin of plugins) {
|
|
11
|
+
for (const [key, fn] of Object.entries(plugin.onPreCommand ?? {})) {
|
|
12
|
+
if (!(key in onPreCommand)) {
|
|
13
|
+
onPreCommand[key] = [];
|
|
14
|
+
}
|
|
15
|
+
onPreCommand[key].push(fn);
|
|
16
|
+
}
|
|
17
|
+
for (const [key, fn] of Object.entries(plugin.onPostCommand ?? {})) {
|
|
18
|
+
if (!(key in onPostCommand)) {
|
|
19
|
+
onPostCommand[key] = [];
|
|
20
|
+
}
|
|
21
|
+
onPostCommand[key].push(fn);
|
|
14
22
|
}
|
|
15
23
|
}
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
const run = async (container, command) => {
|
|
25
|
+
const prefix = command._arguments.filter((a) => a.type === "const").map((a) => a.name);
|
|
26
|
+
for (let i = 0; i <= prefix.length; i++) {
|
|
27
|
+
const key = i === 0 ? "*" : prefix.slice(0, i).map(
|
|
28
|
+
(t, idx) => idx === 0 ? t : t[0].toUpperCase() + t.slice(1)
|
|
29
|
+
).join("");
|
|
30
|
+
const fns = container[key];
|
|
31
|
+
if (fns && fns.length > 0) {
|
|
32
|
+
await Promise.all(fns.map((fn) => fn()));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return {
|
|
37
|
+
async preRun(breadc) {
|
|
38
|
+
for (const p of plugins) {
|
|
39
|
+
await p.onPreRun?.(breadc);
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
async preCommand(command) {
|
|
43
|
+
await run(onPreCommand, command);
|
|
44
|
+
},
|
|
45
|
+
async postCommand(command) {
|
|
46
|
+
await run(onPostCommand, command);
|
|
47
|
+
},
|
|
48
|
+
async postRun(breadc) {
|
|
49
|
+
for (const p of plugins) {
|
|
50
|
+
await p.onPostRun?.(breadc);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
function definePlugin(plugin) {
|
|
56
|
+
return plugin;
|
|
18
57
|
}
|
|
19
58
|
|
|
20
|
-
|
|
21
|
-
|
|
59
|
+
class Token {
|
|
60
|
+
constructor(text) {
|
|
61
|
+
this.text = text;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* @returns Raw argument text
|
|
65
|
+
*/
|
|
66
|
+
raw() {
|
|
67
|
+
return this.text;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* @returns Number representation
|
|
71
|
+
*/
|
|
72
|
+
number() {
|
|
73
|
+
return Number(this.text);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* @returns Remove start - for long or short option
|
|
77
|
+
*/
|
|
78
|
+
option() {
|
|
79
|
+
return this.text.replace(/^-+/, "");
|
|
80
|
+
}
|
|
81
|
+
isOption() {
|
|
82
|
+
return this.type() === "long" || this._type === "short";
|
|
83
|
+
}
|
|
84
|
+
isText() {
|
|
85
|
+
return this.type() === "number" || this._type === "string";
|
|
86
|
+
}
|
|
87
|
+
type() {
|
|
88
|
+
if (this._type) {
|
|
89
|
+
return this._type;
|
|
90
|
+
} else if (this.text === "--") {
|
|
91
|
+
return this._type = "--";
|
|
92
|
+
} else if (this.text === "-") {
|
|
93
|
+
return this._type = "-";
|
|
94
|
+
} else if (!isNaN(Number(this.text))) {
|
|
95
|
+
return this._type = "number";
|
|
96
|
+
} else if (this.text.startsWith("--")) {
|
|
97
|
+
return this._type = "long";
|
|
98
|
+
} else if (this.text.startsWith("-")) {
|
|
99
|
+
return this._type = "short";
|
|
100
|
+
} else {
|
|
101
|
+
return this._type = "string";
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
class Lexer {
|
|
106
|
+
constructor(rawArgs) {
|
|
107
|
+
this.cursor = 0;
|
|
108
|
+
this.rawArgs = rawArgs;
|
|
109
|
+
}
|
|
110
|
+
next() {
|
|
111
|
+
const value = this.rawArgs[this.cursor];
|
|
112
|
+
this.cursor += 1;
|
|
113
|
+
return value ? new Token(value) : void 0;
|
|
114
|
+
}
|
|
115
|
+
hasNext() {
|
|
116
|
+
return this.cursor + 1 < this.rawArgs.length;
|
|
117
|
+
}
|
|
118
|
+
peek() {
|
|
119
|
+
const value = this.rawArgs[this.cursor];
|
|
120
|
+
return value ? new Token(value) : void 0;
|
|
121
|
+
}
|
|
122
|
+
[Symbol.iterator]() {
|
|
123
|
+
const that = this;
|
|
124
|
+
return {
|
|
125
|
+
next() {
|
|
126
|
+
const value = that.rawArgs[that.cursor];
|
|
127
|
+
that.cursor += 1;
|
|
128
|
+
return {
|
|
129
|
+
value: value ? new Token(value) : void 0,
|
|
130
|
+
done: that.cursor > that.rawArgs.length
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
22
136
|
|
|
137
|
+
function camelCase(text) {
|
|
138
|
+
return text.split("-").map((t, idx) => idx === 0 ? t : t[0].toUpperCase() + t.slice(1)).join("");
|
|
139
|
+
}
|
|
23
140
|
function twoColumn(texts, split = " ") {
|
|
24
141
|
const left = padRight(texts.map((t) => t[0]));
|
|
25
142
|
return left.map((l, idx) => l + split + texts[idx][1]);
|
|
@@ -29,505 +146,518 @@ function padRight(texts, fill = " ") {
|
|
|
29
146
|
return texts.map((t) => t + fill.repeat(length - t.length));
|
|
30
147
|
}
|
|
31
148
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
const info = typeof logger === "object" && logger?.info ? logger.info : (message, ...args) => {
|
|
37
|
-
println(`${kolorist.blue("INFO")} ${message}`, ...args);
|
|
38
|
-
};
|
|
39
|
-
const warn = typeof logger === "object" && logger?.warn ? logger.warn : (message, ...args) => {
|
|
40
|
-
println(`${kolorist.yellow("WARN")} ${message}`, ...args);
|
|
41
|
-
};
|
|
42
|
-
const error = typeof logger === "object" && logger?.error ? logger.error : (message, ...args) => {
|
|
43
|
-
println(`${kolorist.red("ERROR")} ${message}`, ...args);
|
|
44
|
-
};
|
|
45
|
-
const debug = typeof logger === "object" && logger?.debug ? logger.debug : (message, ...args) => {
|
|
46
|
-
println(`${kolorist.gray(name)} ${message}`, ...args);
|
|
47
|
-
};
|
|
48
|
-
return {
|
|
49
|
-
println,
|
|
50
|
-
info,
|
|
51
|
-
warn,
|
|
52
|
-
error,
|
|
53
|
-
debug
|
|
54
|
-
};
|
|
149
|
+
class BreadcError extends Error {
|
|
150
|
+
}
|
|
151
|
+
class ParseError extends Error {
|
|
55
152
|
}
|
|
56
153
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
154
|
+
function makeTreeNode(pnode) {
|
|
155
|
+
const node = {
|
|
156
|
+
children: /* @__PURE__ */ new Map(),
|
|
157
|
+
init() {
|
|
158
|
+
},
|
|
159
|
+
next(token, context) {
|
|
160
|
+
const t = token.raw();
|
|
161
|
+
context.result["--"].push(t);
|
|
162
|
+
if (node.children.has(t)) {
|
|
163
|
+
const next = node.children.get(t);
|
|
164
|
+
next.init(context);
|
|
165
|
+
return next;
|
|
64
166
|
} else {
|
|
65
|
-
|
|
167
|
+
return node;
|
|
66
168
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
169
|
+
},
|
|
170
|
+
finish() {
|
|
171
|
+
},
|
|
172
|
+
...pnode
|
|
173
|
+
};
|
|
174
|
+
return node;
|
|
175
|
+
}
|
|
176
|
+
function parseOption(cursor, token, context) {
|
|
177
|
+
const o = token.option();
|
|
178
|
+
const [key, rawV] = o.split("=");
|
|
179
|
+
if (context.options.has(key)) {
|
|
180
|
+
const option = context.options.get(key);
|
|
181
|
+
const name = camelCase(option.name);
|
|
182
|
+
if (option.action) {
|
|
183
|
+
return option.action(cursor, token, context);
|
|
184
|
+
} else if (option.type === "boolean") {
|
|
185
|
+
context.result.options[name] = !key.startsWith("no-") ? true : false;
|
|
186
|
+
} else if (option.type === "string") {
|
|
187
|
+
if (rawV !== void 0) {
|
|
188
|
+
context.result.options[name] = rawV;
|
|
189
|
+
} else {
|
|
190
|
+
const value = context.lexer.next();
|
|
191
|
+
if (value !== void 0 && !value.isOption()) {
|
|
192
|
+
context.result.options[name] = value.raw();
|
|
193
|
+
} else {
|
|
194
|
+
throw new ParseError(
|
|
195
|
+
`You should provide arguments for ${option.format}`
|
|
196
|
+
);
|
|
197
|
+
}
|
|
70
198
|
}
|
|
71
199
|
} else {
|
|
72
|
-
throw new
|
|
200
|
+
throw new ParseError("unreachable");
|
|
201
|
+
}
|
|
202
|
+
if (option.cast) {
|
|
203
|
+
context.result.options[name] = option.cast(context.result.options[name]);
|
|
73
204
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
this.default = config.default;
|
|
77
|
-
this.construct = config.construct;
|
|
205
|
+
} else {
|
|
206
|
+
throw new ParseError(`Unknown option ${token.raw()}`);
|
|
78
207
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
208
|
+
return cursor;
|
|
209
|
+
}
|
|
210
|
+
function parse(root, args) {
|
|
211
|
+
const lexer = new Lexer(args);
|
|
212
|
+
const context = {
|
|
213
|
+
lexer,
|
|
214
|
+
options: /* @__PURE__ */ new Map(),
|
|
215
|
+
result: {
|
|
216
|
+
arguments: [],
|
|
217
|
+
options: {},
|
|
218
|
+
"--": []
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
let cursor = root;
|
|
222
|
+
root.init(context);
|
|
223
|
+
for (const token of lexer) {
|
|
224
|
+
if (token.type() === "--") {
|
|
225
|
+
break;
|
|
226
|
+
} else if (token.isOption()) {
|
|
227
|
+
const res = parseOption(cursor, token, context);
|
|
228
|
+
if (res === false) {
|
|
229
|
+
break;
|
|
230
|
+
} else {
|
|
231
|
+
cursor = res;
|
|
100
232
|
}
|
|
101
|
-
|
|
102
|
-
|
|
233
|
+
} else if (token.isText()) {
|
|
234
|
+
const res = cursor.next(token, context);
|
|
235
|
+
if (res === false) {
|
|
236
|
+
break;
|
|
237
|
+
} else {
|
|
238
|
+
cursor = res;
|
|
103
239
|
}
|
|
240
|
+
} else {
|
|
241
|
+
throw new ParseError("unreachable");
|
|
104
242
|
}
|
|
105
243
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
alias(command) {
|
|
110
|
-
const pieces = command.split(" ").map((t) => t.trim()).filter(Boolean);
|
|
111
|
-
this.prefix.push(pieces);
|
|
112
|
-
return this;
|
|
244
|
+
cursor.finish(context);
|
|
245
|
+
for (const token of lexer) {
|
|
246
|
+
context.result["--"].push(token.raw());
|
|
113
247
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
248
|
+
return {
|
|
249
|
+
command: cursor.command,
|
|
250
|
+
arguments: context.result.arguments,
|
|
251
|
+
options: context.result.options,
|
|
252
|
+
"--": context.result["--"]
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const OptionRE = /^(-[a-zA-Z], )?--([a-zA-Z0-9\-]+)( <[a-zA-Z0-9\-]+>)?$/;
|
|
257
|
+
function makeOption(format, config = {}) {
|
|
258
|
+
let name = "";
|
|
259
|
+
let short = void 0;
|
|
260
|
+
const match = OptionRE.exec(format);
|
|
261
|
+
if (match) {
|
|
262
|
+
name = match[2];
|
|
263
|
+
if (name.startsWith("no-")) {
|
|
264
|
+
throw new BreadcError(`Can not parse option format (${format})`);
|
|
121
265
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return
|
|
266
|
+
if (match[1]) {
|
|
267
|
+
short = match[1][1];
|
|
268
|
+
}
|
|
269
|
+
if (match[3]) {
|
|
270
|
+
const initial = config.default ?? "";
|
|
271
|
+
return {
|
|
272
|
+
format,
|
|
273
|
+
type: "string",
|
|
274
|
+
name,
|
|
275
|
+
short,
|
|
276
|
+
description: config.description ?? "",
|
|
277
|
+
order: 0,
|
|
278
|
+
// @ts-ignore
|
|
279
|
+
initial: config.cast ? config.cast(initial) : initial,
|
|
280
|
+
cast: config.cast
|
|
281
|
+
};
|
|
128
282
|
} else {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
283
|
+
const initial = config.default === void 0 || config.default === null ? false : config.default;
|
|
284
|
+
return {
|
|
285
|
+
format,
|
|
286
|
+
type: "boolean",
|
|
287
|
+
name,
|
|
288
|
+
short,
|
|
289
|
+
description: config.description ?? "",
|
|
290
|
+
order: 0,
|
|
291
|
+
// @ts-ignore
|
|
292
|
+
initial: config.cast ? config.cast(initial) : initial,
|
|
293
|
+
cast: config.cast
|
|
294
|
+
};
|
|
135
295
|
}
|
|
296
|
+
} else {
|
|
297
|
+
throw new BreadcError(`Can not parse option format (${format})`);
|
|
136
298
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
299
|
+
}
|
|
300
|
+
const initContextOptions = (options, context) => {
|
|
301
|
+
for (const option of options) {
|
|
302
|
+
context.options.set(option.name, option);
|
|
303
|
+
if (option.short) {
|
|
304
|
+
context.options.set(option.short, option);
|
|
305
|
+
}
|
|
306
|
+
if (option.type === "boolean") {
|
|
307
|
+
context.options.set("no-" + option.name, option);
|
|
308
|
+
}
|
|
309
|
+
if (option.initial !== void 0) {
|
|
310
|
+
context.result.options[camelCase(option.name)] = option.initial;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
function makeCommand(format, config, root, container) {
|
|
316
|
+
let cursor = root;
|
|
317
|
+
const args = [];
|
|
318
|
+
const options = [];
|
|
319
|
+
const command = {
|
|
320
|
+
callback: void 0,
|
|
321
|
+
format,
|
|
322
|
+
description: config.description ?? "",
|
|
323
|
+
_arguments: args,
|
|
324
|
+
_options: options,
|
|
325
|
+
option(format2, _config, _config2 = {}) {
|
|
326
|
+
const config2 = typeof _config === "string" ? { description: _config, ..._config2 } : _config;
|
|
327
|
+
const option = makeOption(format2, config2);
|
|
328
|
+
options.push(option);
|
|
329
|
+
return command;
|
|
330
|
+
},
|
|
331
|
+
action(fn) {
|
|
332
|
+
command.callback = async (...args2) => {
|
|
333
|
+
await container.preCommand(command);
|
|
334
|
+
const result = await fn(...args2);
|
|
335
|
+
await container.postCommand(command);
|
|
336
|
+
return result;
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const node = makeTreeNode({
|
|
341
|
+
command,
|
|
342
|
+
init(context) {
|
|
343
|
+
initContextOptions(options, context);
|
|
344
|
+
},
|
|
345
|
+
finish(context) {
|
|
346
|
+
const rest = context.result["--"];
|
|
347
|
+
for (let i = 0; i < args.length; i++) {
|
|
348
|
+
if (args[i].type === "const") {
|
|
349
|
+
if (rest[i] !== args[i].name) {
|
|
350
|
+
throw new ParseError(`Sub-command ${args[i].name} mismatch`);
|
|
351
|
+
}
|
|
352
|
+
} else if (args[i].type === "require") {
|
|
353
|
+
if (i >= rest.length) {
|
|
354
|
+
throw new ParseError(
|
|
355
|
+
`You must provide require argument ${args[i].name}`
|
|
356
|
+
);
|
|
357
|
+
}
|
|
358
|
+
context.result.arguments.push(rest[i]);
|
|
359
|
+
} else if (args[i].type === "optional") {
|
|
360
|
+
context.result.arguments.push(rest[i]);
|
|
361
|
+
} else if (args[i].type === "rest") {
|
|
362
|
+
context.result.arguments.push(rest.splice(i));
|
|
144
363
|
}
|
|
145
364
|
}
|
|
146
|
-
|
|
147
|
-
args.splice(0, prefix.length);
|
|
148
|
-
return true;
|
|
149
|
-
}
|
|
365
|
+
context.result["--"] = rest.splice(args.length);
|
|
150
366
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
if (i === this.arguments.length) {
|
|
161
|
-
restArgs.push(...pieces.slice(used).map(String));
|
|
162
|
-
restArgs.push(...(argv["--"] ?? []).map(String));
|
|
163
|
-
} else if (i < pieces.length) {
|
|
164
|
-
if (this.arguments[i].startsWith("[...")) {
|
|
165
|
-
args.push(pieces.slice(i).map(String));
|
|
166
|
-
used = pieces.length;
|
|
167
|
-
} else {
|
|
168
|
-
args.push(String(pieces[i]));
|
|
169
|
-
used++;
|
|
367
|
+
});
|
|
368
|
+
{
|
|
369
|
+
let state = 0;
|
|
370
|
+
for (let i = 0; i < format.length; i++) {
|
|
371
|
+
if (format[i] === "<") {
|
|
372
|
+
if (state !== 0 && state !== 1) {
|
|
373
|
+
throw new BreadcError(
|
|
374
|
+
`Required arguments should be placed before optional or rest arguments`
|
|
375
|
+
);
|
|
170
376
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
377
|
+
const start = i;
|
|
378
|
+
while (i < format.length && format[i] !== ">") {
|
|
379
|
+
i++;
|
|
380
|
+
}
|
|
381
|
+
const name = format.slice(start + 1, i);
|
|
382
|
+
state = 1;
|
|
383
|
+
args.push({ type: "require", name });
|
|
384
|
+
} else if (format[i] === "[") {
|
|
385
|
+
if (state !== 0 && state !== 1) {
|
|
386
|
+
throw new BreadcError(
|
|
387
|
+
`There is at most one optional or rest arguments`
|
|
175
388
|
);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
389
|
+
}
|
|
390
|
+
const start = i;
|
|
391
|
+
while (i < format.length && format[i] !== "]") {
|
|
392
|
+
i++;
|
|
393
|
+
}
|
|
394
|
+
const name = format.slice(start + 1, i);
|
|
395
|
+
state = 2;
|
|
396
|
+
if (name.startsWith("...")) {
|
|
397
|
+
args.push({ type: "rest", name });
|
|
181
398
|
} else {
|
|
182
|
-
|
|
399
|
+
args.push({ type: "optional", name });
|
|
183
400
|
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
}, /* @__PURE__ */ new Map());
|
|
190
|
-
const options = argv;
|
|
191
|
-
delete options["_"];
|
|
192
|
-
for (const [name, rawOption] of fullOptions) {
|
|
193
|
-
if (rawOption.type === "boolean")
|
|
194
|
-
continue;
|
|
195
|
-
if (rawOption.required) {
|
|
196
|
-
if (options[name] === void 0) {
|
|
197
|
-
options[name] = false;
|
|
198
|
-
} else if (options[name] === "") {
|
|
199
|
-
options[name] = true;
|
|
401
|
+
} else if (format[i] !== " ") {
|
|
402
|
+
if (state !== 0) {
|
|
403
|
+
throw new BreadcError(
|
|
404
|
+
`Sub-command should be placed at the beginning`
|
|
405
|
+
);
|
|
200
406
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
} else if (!(name in options)) {
|
|
205
|
-
options[name] = void 0;
|
|
407
|
+
const start = i;
|
|
408
|
+
while (i < format.length && format[i] !== " ") {
|
|
409
|
+
i++;
|
|
206
410
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
411
|
+
const name = format.slice(start, i);
|
|
412
|
+
if (cursor.children.has(name)) {
|
|
413
|
+
cursor = cursor.children.get(name);
|
|
414
|
+
} else {
|
|
415
|
+
const internalNode = makeTreeNode({
|
|
416
|
+
next(token, context) {
|
|
417
|
+
const t = token.raw();
|
|
418
|
+
context.result["--"].push(t);
|
|
419
|
+
if (internalNode.children.has(t)) {
|
|
420
|
+
const next = internalNode.children.get(t);
|
|
421
|
+
next.init(context);
|
|
422
|
+
return next;
|
|
423
|
+
} else {
|
|
424
|
+
throw new ParseError(`Unknown sub-command (${t})`);
|
|
425
|
+
}
|
|
426
|
+
},
|
|
427
|
+
finish() {
|
|
428
|
+
throw new ParseError(`Unknown sub-command`);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
cursor.children.set(name, internalNode);
|
|
432
|
+
cursor = internalNode;
|
|
213
433
|
}
|
|
434
|
+
state = 0;
|
|
435
|
+
args.push({ type: "const", name });
|
|
214
436
|
}
|
|
215
437
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
438
|
+
cursor.command = command;
|
|
439
|
+
if (cursor !== root) {
|
|
440
|
+
for (const [key, value] of cursor.children) {
|
|
441
|
+
node.children.set(key, value);
|
|
219
442
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
options,
|
|
225
|
-
"--": restArgs
|
|
226
|
-
};
|
|
227
|
-
}
|
|
228
|
-
action(fn) {
|
|
229
|
-
this.actionFn = fn;
|
|
230
|
-
}
|
|
231
|
-
async run(...args) {
|
|
232
|
-
if (this.actionFn) {
|
|
233
|
-
return await this.actionFn(...args, {
|
|
234
|
-
logger: this.logger,
|
|
235
|
-
color: kolorist__namespace
|
|
236
|
-
});
|
|
443
|
+
cursor.children = node.children;
|
|
444
|
+
cursor.next = node.next;
|
|
445
|
+
cursor.init = node.init;
|
|
446
|
+
cursor.finish = node.finish;
|
|
237
447
|
} else {
|
|
238
|
-
|
|
239
|
-
`You may miss action function in ${this.format ? `"${this.format}"` : "<default command>"}`
|
|
240
|
-
);
|
|
241
|
-
return void 0;
|
|
448
|
+
cursor.finish = node.finish;
|
|
242
449
|
}
|
|
243
450
|
}
|
|
244
|
-
|
|
245
|
-
let Command = _Command;
|
|
246
|
-
Command.MaxDep = 5;
|
|
247
|
-
class InternalCommand extends Command {
|
|
248
|
-
hasPrefix(_args) {
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
parseArgs(args, _globalOptions) {
|
|
252
|
-
const argumentss = args["_"];
|
|
253
|
-
const options = args;
|
|
254
|
-
delete options["_"];
|
|
255
|
-
delete options["help"];
|
|
256
|
-
delete options["version"];
|
|
257
|
-
return {
|
|
258
|
-
command: this,
|
|
259
|
-
arguments: argumentss,
|
|
260
|
-
options: args,
|
|
261
|
-
"--": []
|
|
262
|
-
};
|
|
263
|
-
}
|
|
451
|
+
return command;
|
|
264
452
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
this.runCommands.push(cmd);
|
|
281
|
-
} else if (cmd.hasPrefix(args)) {
|
|
282
|
-
this.helpCommands.push(cmd);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
return true;
|
|
288
|
-
} else {
|
|
289
|
-
return false;
|
|
453
|
+
function makeVersionCommand(name, config) {
|
|
454
|
+
const command = {
|
|
455
|
+
callback() {
|
|
456
|
+
const text = `${name}/${config.version ? config.version : "unknown"}`;
|
|
457
|
+
console.log(text);
|
|
458
|
+
return text;
|
|
459
|
+
},
|
|
460
|
+
format: "-v, --version",
|
|
461
|
+
description: "Print version",
|
|
462
|
+
_arguments: [],
|
|
463
|
+
_options: [],
|
|
464
|
+
option() {
|
|
465
|
+
return command;
|
|
466
|
+
},
|
|
467
|
+
action() {
|
|
290
468
|
}
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
this.logger.println(line);
|
|
296
|
-
}
|
|
297
|
-
this.runCommands.splice(0);
|
|
298
|
-
this.helpCommands.splice(0);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
class VersionCommand extends InternalCommand {
|
|
302
|
-
constructor(version, logger) {
|
|
303
|
-
super("-v, --version", { description: "Display version number", logger });
|
|
304
|
-
this.version = version;
|
|
305
|
-
}
|
|
306
|
-
shouldRun(args) {
|
|
307
|
-
const isEmpty = !args["_"].length && !args["--"]?.length;
|
|
308
|
-
if (args.version && isEmpty) {
|
|
309
|
-
return true;
|
|
310
|
-
} else if (args.v && isEmpty) {
|
|
311
|
-
return true;
|
|
312
|
-
} else {
|
|
469
|
+
};
|
|
470
|
+
const node = makeTreeNode({
|
|
471
|
+
command,
|
|
472
|
+
next() {
|
|
313
473
|
return false;
|
|
314
474
|
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
475
|
+
});
|
|
476
|
+
const option = {
|
|
477
|
+
format: "-v, --version",
|
|
478
|
+
name: "version",
|
|
479
|
+
short: "v",
|
|
480
|
+
type: "boolean",
|
|
481
|
+
initial: void 0,
|
|
482
|
+
order: 999999999 + 1,
|
|
483
|
+
description: "Print version",
|
|
484
|
+
action() {
|
|
485
|
+
return node;
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
return option;
|
|
322
489
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
new HelpCommand(this.commands, this.help.bind(this), this.logger)
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
version() {
|
|
342
|
-
return `${this.name}/${this._version}`;
|
|
343
|
-
}
|
|
344
|
-
help(commands = []) {
|
|
345
|
-
const output = [];
|
|
346
|
-
const println = (msg) => output.push(msg);
|
|
347
|
-
println(this.version());
|
|
348
|
-
if (commands.length === 0) {
|
|
349
|
-
if (this.description) {
|
|
350
|
-
println("");
|
|
351
|
-
if (Array.isArray(this.description)) {
|
|
352
|
-
for (const line of this.description) {
|
|
353
|
-
println(line);
|
|
354
|
-
}
|
|
355
|
-
} else {
|
|
356
|
-
println(this.description);
|
|
490
|
+
function makeHelpCommand(name, config) {
|
|
491
|
+
function expandMessage(message) {
|
|
492
|
+
const result = [];
|
|
493
|
+
for (const row of message) {
|
|
494
|
+
if (typeof row === "function") {
|
|
495
|
+
const r = row();
|
|
496
|
+
if (r) {
|
|
497
|
+
result.push(...expandMessage(r));
|
|
498
|
+
}
|
|
499
|
+
} else if (typeof row === "string") {
|
|
500
|
+
result.push(row);
|
|
501
|
+
} else if (Array.isArray(row)) {
|
|
502
|
+
const lines = twoColumn(row);
|
|
503
|
+
for (const line of lines) {
|
|
504
|
+
result.push(line);
|
|
357
505
|
}
|
|
358
506
|
}
|
|
359
|
-
if (this.defaultCommand) {
|
|
360
|
-
println(``);
|
|
361
|
-
println(`Usage:`);
|
|
362
|
-
println(` $ ${this.name} ${this.defaultCommand.format}`);
|
|
363
|
-
}
|
|
364
|
-
} else if (commands.length === 1) {
|
|
365
|
-
const command = commands[0];
|
|
366
|
-
if (command.description) {
|
|
367
|
-
println("");
|
|
368
|
-
println(command.description);
|
|
369
|
-
}
|
|
370
|
-
println(``);
|
|
371
|
-
println(`Usage:`);
|
|
372
|
-
println(` $ ${this.name} ${command.format}`);
|
|
373
|
-
}
|
|
374
|
-
if (commands.length !== 1) {
|
|
375
|
-
const cmdList = (commands.length === 0 ? this.commands : commands).filter(
|
|
376
|
-
(c) => !c.isInternal
|
|
377
|
-
);
|
|
378
|
-
println(``);
|
|
379
|
-
println(`Commands:`);
|
|
380
|
-
const commandHelps = cmdList.map(
|
|
381
|
-
(c) => [` $ ${this.name} ${c.format}`, c.description]
|
|
382
|
-
);
|
|
383
|
-
for (const line of twoColumn(commandHelps)) {
|
|
384
|
-
println(line);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
println(``);
|
|
388
|
-
println(`Options:`);
|
|
389
|
-
const optionHelps = [].concat([
|
|
390
|
-
...commands.length > 0 ? commands.flatMap(
|
|
391
|
-
(cmd) => cmd.options.map(
|
|
392
|
-
(o) => [` ${o.format}`, o.description]
|
|
393
|
-
)
|
|
394
|
-
) : [],
|
|
395
|
-
...this.options.map(
|
|
396
|
-
(o) => [` ${o.format}`, o.description]
|
|
397
|
-
),
|
|
398
|
-
[` -h, --help`, `Display this message`],
|
|
399
|
-
[` -v, --version`, `Display version number`]
|
|
400
|
-
]);
|
|
401
|
-
for (const line of twoColumn(optionHelps)) {
|
|
402
|
-
println(line);
|
|
403
|
-
}
|
|
404
|
-
println(``);
|
|
405
|
-
return output;
|
|
406
|
-
}
|
|
407
|
-
option(format, configOrDescription = "", otherConfig = {}) {
|
|
408
|
-
const config = typeof configOrDescription === "object" ? configOrDescription : { ...otherConfig, description: configOrDescription };
|
|
409
|
-
try {
|
|
410
|
-
const option = new Option(format, config);
|
|
411
|
-
this.options.push(option);
|
|
412
|
-
} catch (error) {
|
|
413
|
-
this.logger.warn(error.message);
|
|
414
|
-
}
|
|
415
|
-
return this;
|
|
416
|
-
}
|
|
417
|
-
command(format, configOrDescription = "", otherConfig = {}) {
|
|
418
|
-
const config = typeof configOrDescription === "object" ? configOrDescription : { ...otherConfig, description: configOrDescription };
|
|
419
|
-
const command = new Command(format, { ...config, logger: this.logger });
|
|
420
|
-
if (command.default) {
|
|
421
|
-
if (this.defaultCommand) {
|
|
422
|
-
this.logger.warn("You can not have two default commands.");
|
|
423
|
-
}
|
|
424
|
-
this.defaultCommand = command;
|
|
425
507
|
}
|
|
426
|
-
|
|
427
|
-
return command;
|
|
508
|
+
return result;
|
|
428
509
|
}
|
|
429
|
-
|
|
430
|
-
const
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
{
|
|
435
|
-
const
|
|
436
|
-
for (const
|
|
437
|
-
if (
|
|
438
|
-
|
|
439
|
-
if (
|
|
440
|
-
|
|
510
|
+
function expandCommands(cursor) {
|
|
511
|
+
const visited = /* @__PURE__ */ new WeakSet();
|
|
512
|
+
const commands = cursor.command ? [cursor.command] : [];
|
|
513
|
+
const q = [cursor];
|
|
514
|
+
visited.add(cursor);
|
|
515
|
+
for (let i = 0; i < q.length; i++) {
|
|
516
|
+
const cur = q[i];
|
|
517
|
+
for (const [_key, cmd] of cur.children) {
|
|
518
|
+
if (!visited.has(cmd)) {
|
|
519
|
+
visited.add(cmd);
|
|
520
|
+
if (cmd.command) {
|
|
521
|
+
commands.push(cmd.command);
|
|
441
522
|
}
|
|
442
|
-
|
|
443
|
-
names.set(option.name, option);
|
|
523
|
+
q.push(cmd);
|
|
444
524
|
}
|
|
445
525
|
}
|
|
446
526
|
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
).reduce((map, o) => {
|
|
459
|
-
map[o.name] = o.default;
|
|
460
|
-
return map;
|
|
461
|
-
}, {});
|
|
462
|
-
const argv = minimist__default(args, {
|
|
463
|
-
string: allowOptions.filter((o) => o.type === "string").map((o) => o.name),
|
|
464
|
-
boolean: allowOptions.filter((o) => o.type === "boolean").map((o) => o.name).concat(["help", "version"]),
|
|
465
|
-
default: defaultValue,
|
|
466
|
-
alias,
|
|
467
|
-
"--": true,
|
|
468
|
-
unknown: (t) => {
|
|
469
|
-
if (t[0] !== "-")
|
|
470
|
-
return true;
|
|
471
|
-
else {
|
|
472
|
-
if (["--help", "-h", "--version", "-v"].includes(t)) {
|
|
473
|
-
return true;
|
|
527
|
+
return commands;
|
|
528
|
+
}
|
|
529
|
+
const command = {
|
|
530
|
+
callback(option2) {
|
|
531
|
+
const context = option2.__context__;
|
|
532
|
+
const cursor = option2.__cursor__;
|
|
533
|
+
const output = [
|
|
534
|
+
`${name}/${config.version ? config.version : "unknown"}`,
|
|
535
|
+
() => {
|
|
536
|
+
if (config.description) {
|
|
537
|
+
return ["", config.description];
|
|
474
538
|
} else {
|
|
475
|
-
|
|
476
|
-
return false;
|
|
539
|
+
return void 0;
|
|
477
540
|
}
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
541
|
+
},
|
|
542
|
+
() => {
|
|
543
|
+
const cmds = expandCommands(cursor);
|
|
544
|
+
if (cmds.length > 0) {
|
|
545
|
+
return [
|
|
546
|
+
"",
|
|
547
|
+
color.bold(color.underline("Commands:")),
|
|
548
|
+
cmds.map((cmd) => [
|
|
549
|
+
` ${color.bold(name)} ${color.bold(cmd.format)}`,
|
|
550
|
+
cmd.description
|
|
551
|
+
])
|
|
552
|
+
];
|
|
553
|
+
} else {
|
|
554
|
+
return void 0;
|
|
555
|
+
}
|
|
556
|
+
},
|
|
557
|
+
"",
|
|
558
|
+
color.bold(color.underline("Options:")),
|
|
559
|
+
[...context.options.entries()].filter(([key, op]) => key === op.name).sort((lhs, rhs) => lhs[1].order - rhs[1].order).map(([_key, op]) => [
|
|
560
|
+
" " + (!op.short ? " " : "") + color.bold(op.format),
|
|
561
|
+
op.description
|
|
562
|
+
]),
|
|
563
|
+
""
|
|
564
|
+
];
|
|
565
|
+
const text = expandMessage(output).join("\n");
|
|
566
|
+
console.log(text);
|
|
567
|
+
return text;
|
|
568
|
+
},
|
|
569
|
+
format: "-h, --help",
|
|
570
|
+
description: "Print help",
|
|
571
|
+
_arguments: [],
|
|
572
|
+
_options: [],
|
|
573
|
+
option() {
|
|
574
|
+
return command;
|
|
575
|
+
},
|
|
576
|
+
action() {
|
|
483
577
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
578
|
+
};
|
|
579
|
+
const node = makeTreeNode({
|
|
580
|
+
command,
|
|
581
|
+
next() {
|
|
582
|
+
return false;
|
|
488
583
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
584
|
+
});
|
|
585
|
+
const option = {
|
|
586
|
+
format: "-h, --help",
|
|
587
|
+
name: "help",
|
|
588
|
+
short: "h",
|
|
589
|
+
type: "boolean",
|
|
590
|
+
initial: void 0,
|
|
591
|
+
description: "Print help",
|
|
592
|
+
order: 999999999,
|
|
593
|
+
action(cursor, _token, context) {
|
|
594
|
+
context.result.options.__cursor__ = cursor;
|
|
595
|
+
context.result.options.__context__ = context;
|
|
596
|
+
return node;
|
|
492
597
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
const parsed = this.parse(args);
|
|
511
|
-
if (parsed.command) {
|
|
512
|
-
await Promise.all(
|
|
513
|
-
this.callbacks.pre.map((fn) => fn(parsed.options))
|
|
514
|
-
);
|
|
515
|
-
const returnValue = await parsed.command.run(...parsed.arguments, {
|
|
516
|
-
"--": parsed["--"],
|
|
517
|
-
...parsed.options
|
|
518
|
-
});
|
|
519
|
-
await Promise.all(
|
|
520
|
-
this.callbacks.post.map((fn) => fn(parsed.options))
|
|
598
|
+
};
|
|
599
|
+
return option;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function breadc(name, config = {}) {
|
|
603
|
+
let defaultCommand = void 0;
|
|
604
|
+
const globalOptions = [];
|
|
605
|
+
const container = makePluginContainer(config.plugins);
|
|
606
|
+
const root = makeTreeNode({
|
|
607
|
+
init(context) {
|
|
608
|
+
initContextOptions(globalOptions, context);
|
|
609
|
+
if (defaultCommand) {
|
|
610
|
+
initContextOptions(defaultCommand._options, context);
|
|
611
|
+
}
|
|
612
|
+
initContextOptions(
|
|
613
|
+
[makeHelpCommand(name, config), makeVersionCommand(name, config)],
|
|
614
|
+
context
|
|
521
615
|
);
|
|
522
|
-
|
|
523
|
-
|
|
616
|
+
},
|
|
617
|
+
finish() {
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
const breadc2 = {
|
|
621
|
+
option(format, _config, _config2 = {}) {
|
|
622
|
+
const config2 = typeof _config === "string" ? { description: _config, ..._config2 } : _config;
|
|
623
|
+
const option = makeOption(format, config2);
|
|
624
|
+
globalOptions.push(option);
|
|
625
|
+
return breadc2;
|
|
626
|
+
},
|
|
627
|
+
command(text, _config = {}) {
|
|
628
|
+
const config2 = typeof _config === "string" ? { description: _config } : _config;
|
|
629
|
+
const command = makeCommand(text, config2, root, container);
|
|
630
|
+
if (command._arguments.length === 0 || command._arguments[0].type !== "const") {
|
|
631
|
+
defaultCommand = command;
|
|
632
|
+
}
|
|
633
|
+
return command;
|
|
634
|
+
},
|
|
635
|
+
parse(args) {
|
|
636
|
+
const result = parse(root, args);
|
|
637
|
+
return result;
|
|
638
|
+
},
|
|
639
|
+
async run(args) {
|
|
640
|
+
const result = breadc2.parse(args);
|
|
641
|
+
const command = result.command;
|
|
642
|
+
if (command) {
|
|
643
|
+
if (command.callback) {
|
|
644
|
+
await container.preRun(breadc2);
|
|
645
|
+
const r = command.callback(...result.arguments, {
|
|
646
|
+
...result.options,
|
|
647
|
+
"--": result["--"]
|
|
648
|
+
});
|
|
649
|
+
await container.postRun(breadc2);
|
|
650
|
+
return r;
|
|
651
|
+
}
|
|
652
|
+
}
|
|
524
653
|
return void 0;
|
|
525
654
|
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
function breadc(name, option = {}) {
|
|
530
|
-
return new Breadc(name, option);
|
|
655
|
+
};
|
|
656
|
+
return breadc2;
|
|
531
657
|
}
|
|
532
658
|
|
|
533
|
-
|
|
659
|
+
exports.BreadcError = BreadcError;
|
|
660
|
+
exports.ParseError = ParseError;
|
|
661
|
+
exports.breadc = breadc;
|
|
662
|
+
exports.default = breadc;
|
|
663
|
+
exports.definePlugin = definePlugin;
|