termkit 1.7.0 → 2.0.1

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/dist/index.mjs ADDED
@@ -0,0 +1,393 @@
1
+ // src/models/Command.ts
2
+ import cosmetic from "cosmetic";
3
+
4
+ // src/helpers/findCommand.ts
5
+ function findCommand(array, commands) {
6
+ for (const command2 of commands) {
7
+ if (array[0] === command2.name) {
8
+ array.shift();
9
+ return command2;
10
+ }
11
+ }
12
+ return null;
13
+ }
14
+
15
+ // src/helpers/findVariable.ts
16
+ function coerce(value, type) {
17
+ if (type === "number") return Number(value);
18
+ if (type === "boolean") return value === "true";
19
+ return value;
20
+ }
21
+ function findVariable(array, variable, commands) {
22
+ if (variable.array) {
23
+ const result = [];
24
+ while (array.length > 0 && !array[0].startsWith("-")) {
25
+ if (commands.includes(array[0])) break;
26
+ result.push(coerce(array.shift(), variable.type));
27
+ }
28
+ if (result.length === 0 && variable.required) throw new Error(`Missing required variable <${variable.name}>`);
29
+ return result.length > 0 ? result : true;
30
+ }
31
+ if (array.length > 0 && !array[0].startsWith("-")) {
32
+ if ((!commands.includes(array[0]) || variable.required) && array[0] !== "help") {
33
+ return coerce(array.shift(), variable.type);
34
+ }
35
+ }
36
+ if (variable.required) throw new Error(`Missing required variable <${variable.name}>`);
37
+ return true;
38
+ }
39
+
40
+ // src/helpers/findCommandVariables.ts
41
+ function findCommandVariables(array, command2) {
42
+ if (!command2.variables) return null;
43
+ const result = {};
44
+ for (const variable of command2.variables) {
45
+ const value = findVariable(array, variable, command2.commandStrings);
46
+ if (value !== true) result[variable.name] = value;
47
+ }
48
+ return Object.keys(result).length > 0 ? result : null;
49
+ }
50
+
51
+ // src/helpers/findOption.ts
52
+ function findOption(string, options) {
53
+ return options.find((o) => o.short === string || o.long === string);
54
+ }
55
+
56
+ // src/helpers/findVariables.ts
57
+ function findVariables(base2, array, variables, commands) {
58
+ const result = {};
59
+ if (!variables) {
60
+ if (base2) result[base2] = true;
61
+ return result;
62
+ }
63
+ if (variables.length > 1 && base2) result[base2] = {};
64
+ for (const variable of variables) {
65
+ const value = findVariable(array, variable, commands);
66
+ if (variables.length > 1 && base2) {
67
+ ;
68
+ result[base2][variable.name] = value;
69
+ } else if (base2) {
70
+ result[base2] = value;
71
+ } else {
72
+ result[variable.name] = value;
73
+ }
74
+ }
75
+ return result;
76
+ }
77
+
78
+ // src/helpers/findOptions.ts
79
+ function findOptions(array, command2) {
80
+ const result = {};
81
+ while (array.length > 0 && array[0].startsWith("-")) {
82
+ if (array[0].startsWith("--")) {
83
+ const raw = array.shift().slice(2);
84
+ const option2 = findOption(raw, command2.optionsArray);
85
+ if (!option2) throw new Error(`Unknown Option: --${raw}`);
86
+ try {
87
+ Object.assign(result, findVariables(option2.long, array, option2.variables, command2.commandStrings));
88
+ } catch (err) {
89
+ ;
90
+ err.message += ` for --${option2.long}`;
91
+ throw err;
92
+ }
93
+ } else {
94
+ let string = array.shift();
95
+ const short = string.slice(1, 2);
96
+ const option2 = findOption(short, command2.optionsArray);
97
+ if (!option2) throw new Error(`Unknown Option: -${short}`);
98
+ string = string.replace(short, "");
99
+ if (string !== "-") array.unshift(string);
100
+ try {
101
+ Object.assign(result, findVariables(option2.long, array, option2.variables, command2.commandStrings));
102
+ } catch (err) {
103
+ ;
104
+ err.message += ` for --${option2.long}`;
105
+ throw err;
106
+ }
107
+ }
108
+ }
109
+ return result;
110
+ }
111
+
112
+ // src/models/Variable.ts
113
+ var Variable = class {
114
+ constructor(data) {
115
+ this.array = false;
116
+ this.name = null;
117
+ this.raw = null;
118
+ this.required = false;
119
+ this.type = "string";
120
+ this.value = null;
121
+ if (!data) return;
122
+ if (data.array) this.array = data.array;
123
+ if (data.name) this.name = data.name;
124
+ if (data.raw) this.raw = data.raw;
125
+ if (data.required) this.required = data.required;
126
+ if (data.type) this.type = data.type;
127
+ if (this.array) this.value = [];
128
+ }
129
+ };
130
+
131
+ // src/helpers/getVariables.ts
132
+ function parseName(raw) {
133
+ const colon = raw.indexOf(":");
134
+ if (colon === -1) return { name: raw, type: "string" };
135
+ return {
136
+ name: raw.slice(0, colon),
137
+ type: raw.slice(colon + 1)
138
+ };
139
+ }
140
+ function getVariables(string) {
141
+ const results = [];
142
+ for (const part of string.split(" ")) {
143
+ const trimmed = part.trim();
144
+ if (!trimmed) continue;
145
+ if (trimmed.startsWith("<") && trimmed.endsWith(">")) {
146
+ const { name, type } = parseName(trimmed.slice(1, -1));
147
+ results.push(new Variable({ name, raw: trimmed, required: true, type }));
148
+ } else if (trimmed.startsWith("[") && trimmed.endsWith("...]")) {
149
+ const { name, type } = parseName(trimmed.slice(1, -4));
150
+ results.push(new Variable({ array: true, name, raw: trimmed, type }));
151
+ } else if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
152
+ const { name, type } = parseName(trimmed.slice(1, -1));
153
+ results.push(new Variable({ name, raw: trimmed, type }));
154
+ } else {
155
+ throw new Error(`Unrecognized variable description: ${trimmed}`);
156
+ }
157
+ }
158
+ return results;
159
+ }
160
+
161
+ // src/models/Option.ts
162
+ var Option = class {
163
+ constructor(data) {
164
+ this.short = null;
165
+ this.long = null;
166
+ this.info = null;
167
+ this.variables = null;
168
+ if (!data) return;
169
+ if (data.short) this.short = data.short;
170
+ if (data.long) this.long = data.long;
171
+ if (data.info) this.info = data.info;
172
+ if (data.variables) this.variables = getVariables(data.variables);
173
+ }
174
+ description(info) {
175
+ this.info = info;
176
+ return this;
177
+ }
178
+ };
179
+
180
+ // src/models/Command.ts
181
+ var Command = class {
182
+ constructor(data) {
183
+ this.actionFunction = null;
184
+ this.commandsArray = [];
185
+ this.commandStrings = ["help"];
186
+ this.info = null;
187
+ this.middlewaresArray = [];
188
+ this.name = null;
189
+ this.optionsArray = [];
190
+ this.variables = null;
191
+ this.versionString = null;
192
+ if (!data) return;
193
+ if (data.info) this.info = data.info;
194
+ if (data.name) this.name = data.name;
195
+ if (data.variables) this.variables = getVariables(data.variables);
196
+ if (data.middlewares) this.middlewaresArray = [...data.middlewares];
197
+ if (data.options) this.optionsArray = [...data.options];
198
+ }
199
+ description(info) {
200
+ this.info = info;
201
+ return this;
202
+ }
203
+ variable(string) {
204
+ const vars = getVariables(string);
205
+ if (!this.variables) {
206
+ this.variables = vars;
207
+ } else {
208
+ this.variables.push(...vars);
209
+ }
210
+ return this;
211
+ }
212
+ action(fn) {
213
+ this.actionFunction = fn;
214
+ return this;
215
+ }
216
+ command(cmd) {
217
+ this.commandsArray.push(cmd);
218
+ return this;
219
+ }
220
+ commands(cmds) {
221
+ this.commandsArray = [...cmds];
222
+ for (const cmd of cmds) {
223
+ if (cmd.name) this.commandStrings.unshift(cmd.name);
224
+ }
225
+ return this;
226
+ }
227
+ middleware(fn) {
228
+ this.middlewaresArray.push(fn);
229
+ return this;
230
+ }
231
+ middlewares(fns) {
232
+ this.middlewaresArray.push(...fns);
233
+ return this;
234
+ }
235
+ option(short, long, variables, info) {
236
+ this.optionsArray.push(new Option({ short, long, variables, info }));
237
+ return this;
238
+ }
239
+ options(opts) {
240
+ this.optionsArray.push(...opts);
241
+ return this;
242
+ }
243
+ version(v) {
244
+ this.versionString = v;
245
+ return this;
246
+ }
247
+ help(_source) {
248
+ const table = [];
249
+ let program = this.name ?? "Program";
250
+ if (this.variables) for (const v of this.variables) program += ` ${v.raw}`;
251
+ if (this.optionsArray.length > 0) program += " [...options]";
252
+ table.push({ title: "\nCommand", info: program, data: [] });
253
+ if (this.versionString) table.push({ title: "Version", info: `v${this.versionString}`, data: [] });
254
+ if (this.info) table.push({ title: "Info", info: this.info, data: [] });
255
+ if (this.optionsArray.length > 0) {
256
+ const section = { title: "Options", data: [] };
257
+ for (const opt of this.optionsArray) {
258
+ let name = "";
259
+ if (opt.short) name = `-${opt.short}`;
260
+ if (opt.short && opt.long) name += ", ";
261
+ if (opt.long) name += `--${opt.long}`;
262
+ if (opt.variables) for (const v of opt.variables) name += ` ${v.raw}`;
263
+ section.data.push([name, opt.info ?? ""]);
264
+ }
265
+ table.push(section);
266
+ }
267
+ if (this.commandsArray.length > 0) {
268
+ const section = { title: "Subcommands", data: [] };
269
+ for (const cmd of this.commandsArray) {
270
+ let name = cmd.name ?? "";
271
+ if (cmd.variables) for (const v of cmd.variables) name += ` ${v.raw}`;
272
+ section.data.push([name, cmd.info ?? ""]);
273
+ }
274
+ table.push(section);
275
+ }
276
+ const padding = {};
277
+ for (const section of table) {
278
+ for (const row of section.data) {
279
+ for (const [i, s] of row.entries()) {
280
+ if (padding[i] === void 0 || s.length > padding[i]) padding[i] = s.length;
281
+ }
282
+ }
283
+ }
284
+ const lines = [];
285
+ for (const section of table) {
286
+ lines.push(section.title ? cosmetic.cyan.underline.encoder(section.title) : "");
287
+ if (section.info) lines.push(section.info);
288
+ for (const row of section.data) {
289
+ let line = "";
290
+ for (const [i, s] of row.entries()) {
291
+ const padded = padding[i] !== void 0 ? s.padEnd(padding[i]) : s;
292
+ line = line ? `${line} ${padded}` : padded;
293
+ }
294
+ lines.push(line);
295
+ }
296
+ lines.push("");
297
+ }
298
+ for (const line of lines) console.log(line);
299
+ }
300
+ async parse(input) {
301
+ const array = [...input];
302
+ array.splice(0, 2);
303
+ let command2 = this;
304
+ const options = { _source: Array.from(array) };
305
+ while (array.length) {
306
+ if (!array.includes("help")) {
307
+ Object.assign(options, findOptions(array, command2));
308
+ const cmdVars = findCommandVariables(array, command2);
309
+ if (cmdVars) Object.assign(options, cmdVars);
310
+ Object.assign(options, findOptions(array, command2));
311
+ }
312
+ if (array.length) {
313
+ if (!array.includes("help")) {
314
+ for (const mw of command2.middlewaresArray) await mw(options);
315
+ }
316
+ const next = findCommand(array, command2.commandsArray);
317
+ if (!next && array[0] === "help") return command2.help(options._source);
318
+ if (!next) throw new SyntaxError(`Unknown command: ${array[0]}`);
319
+ const name = command2.name ?? "_base";
320
+ if (!options._parents) options._parents = {};
321
+ options._parents[name] = {};
322
+ for (const key of Object.keys(options)) {
323
+ if (!key.startsWith("_")) {
324
+ options._parents[name][key] = options[key];
325
+ delete options[key];
326
+ }
327
+ }
328
+ command2 = next;
329
+ }
330
+ }
331
+ for (const mw of command2.middlewaresArray) await mw(options);
332
+ if (command2.actionFunction) return command2.actionFunction(options);
333
+ if (options._source.length === 2) return command2.help(options._source);
334
+ throw new Error(`No action for command: ${command2.name ?? "_base"}`);
335
+ }
336
+ };
337
+
338
+ // src/models/Termkit.ts
339
+ var _Termkit = class _Termkit {
340
+ static set defaults(obj) {
341
+ _Termkit.commandDefaults = obj;
342
+ }
343
+ static setDefaults(obj) {
344
+ _Termkit.commandDefaults = obj;
345
+ }
346
+ static command(name, variables, info) {
347
+ const cmd = new Command(Object.assign({ name, variables, info }, _Termkit.commandDefaults));
348
+ if (!_Termkit.base) _Termkit.base = cmd;
349
+ return cmd;
350
+ }
351
+ static middleware(action) {
352
+ return action;
353
+ }
354
+ static option(short, long, variables, info) {
355
+ return new Option({ short, long, variables, info });
356
+ }
357
+ static parse(arr) {
358
+ if (!_Termkit.base) throw new Error("No command defined");
359
+ return _Termkit.base.parse(arr);
360
+ }
361
+ };
362
+ _Termkit.base = null;
363
+ _Termkit.commandDefaults = {};
364
+ var Termkit = _Termkit;
365
+
366
+ // src/index.ts
367
+ var base = null;
368
+ var commandDefaults = {};
369
+ var command = (name, variables, info) => {
370
+ const cmd = new Command(Object.assign({ name, variables, info }, commandDefaults));
371
+ if (!base) base = cmd;
372
+ return cmd;
373
+ };
374
+ var middleware = (fn) => fn;
375
+ var option = (short, long, variables, info) => new Option({ short, long, variables, info });
376
+ var parse = (arr) => {
377
+ if (!base) throw new Error("No command defined");
378
+ return base.parse(arr);
379
+ };
380
+ var setDefaults = (data) => {
381
+ commandDefaults = data;
382
+ };
383
+ export {
384
+ Command,
385
+ Option,
386
+ Termkit,
387
+ Variable,
388
+ command,
389
+ middleware,
390
+ option,
391
+ parse,
392
+ setDefaults
393
+ };
package/package.json CHANGED
@@ -1,27 +1,75 @@
1
1
  {
2
2
  "name": "termkit",
3
- "version": "1.7.0",
4
- "description": "Terminal command constructor and parser kit",
5
- "main": "index.js",
6
- "scripts": {
7
- "test": "mocha ./test; exit 0"
8
- },
3
+ "version": "2.0.1",
4
+ "description": "Fluent CLI framework for Node.js with nested subcommands, middleware, and TypeScript support",
9
5
  "keywords": [
10
- "terminal",
11
- "command",
12
- "line",
13
- "input",
14
- "constructor",
15
- "parser"
6
+ "argv",
7
+ "builder",
8
+ "cli",
9
+ "command-line",
10
+ "framework",
11
+ "middleware",
12
+ "parser",
13
+ "subcommands",
14
+ "typescript"
16
15
  ],
16
+ "homepage": "https://github.com/jayrdeaton/termkit#readme",
17
+ "bugs": {
18
+ "url": "https://github.com/jayrdeaton/termkit/issues"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/jayrdeaton/termkit.git"
23
+ },
24
+ "license": "MIT",
17
25
  "author": "Jay Deaton",
18
- "license": "ISC",
26
+ "sideEffects": false,
27
+ "type": "commonjs",
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.ts",
31
+ "import": "./dist/index.mjs",
32
+ "require": "./dist/index.js"
33
+ }
34
+ },
35
+ "main": "dist/index.js",
36
+ "module": "dist/index.mjs",
37
+ "types": "dist/index.d.ts",
38
+ "files": [
39
+ "dist"
40
+ ],
41
+ "scripts": {
42
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
43
+ "fix": "eslint --fix",
44
+ "lint": "eslint",
45
+ "prepublishOnly": "npm run build",
46
+ "release": "git push --follow-tags",
47
+ "release:major": "npm version major && git push --follow-tags",
48
+ "release:minor": "npm version minor && git push --follow-tags",
49
+ "release:patch": "npm version patch && git push --follow-tags",
50
+ "test": "jest",
51
+ "test:watch": "jest --watchAll",
52
+ "typecheck": "tsc --noEmit",
53
+ "preversion": "npm run lint && npm test"
54
+ },
19
55
  "dependencies": {
20
- "cosmetic": "^1.2.1"
56
+ "cosmetic": "^1.4.0"
21
57
  },
22
58
  "devDependencies": {
23
- "amprisand": "^0.1.3",
24
- "faker": "^4.1.0",
25
- "mocha": "^8.1.3"
59
+ "@types/jest": "^30.0.0",
60
+ "@types/node": "^25.9.1",
61
+ "@typescript-eslint/parser": "^8.60.0",
62
+ "eslint": "^10.4.1",
63
+ "eslint-config-prettier": "^10.1.8",
64
+ "eslint-plugin-package-json": "^1.2.0",
65
+ "eslint-plugin-prettier": "^5.5.6",
66
+ "eslint-plugin-simple-import-sort": "^13.0.0",
67
+ "jest": "^30.4.2",
68
+ "prettier": "^3.8.3",
69
+ "ts-jest": "^29.4.11",
70
+ "ts-node": "^10.9.2",
71
+ "tsup": "^8.5.1",
72
+ "typescript": "^6.0.3",
73
+ "typescript-eslint": "^8.60.0"
26
74
  }
27
75
  }
package/index.js DELETED
@@ -1,17 +0,0 @@
1
- const { models } = require('./src'),
2
- { Command, Middleware, Option, Termkit } = models
3
-
4
- let base
5
- let command_defaults = {}
6
-
7
- const command = (name, variables, info) => {
8
- const command = new Command(Object.assign({ name, variables, info }, command_defaults))
9
- if (!base) base = command
10
- return command
11
- }
12
- const setDefaults = data => command_defaults = data
13
- const middleware = (data) => data
14
- const option = (short, long, variables, info) => new Option({ short, long, variables, info })
15
- const parse = (array) => base.parse(array)
16
-
17
- module.exports = { command, middleware, option, parse, setDefaults }
File without changes
@@ -1,10 +0,0 @@
1
- module.exports = (array, commands) => {
2
- let result
3
- for (let command of commands) {
4
- if (array[0] === command.name) {
5
- array.shift()
6
- return command
7
- }
8
- }
9
- return null
10
- }
@@ -1,11 +0,0 @@
1
- const findVariables = require('./findVariables')
2
-
3
- module.exports = (array, command) => {
4
- let variables = findVariables(null, array, command.variables, command.commandStrings)
5
- if (variables[null]) variables = variables[null]
6
- for (let key of Object.keys(variables)) {
7
- if (variables[key] === true) delete variables[key]
8
- }
9
- if (Object.keys(variables).length === 0) return null
10
- return variables
11
- }
@@ -1 +0,0 @@
1
- module.exports = (string, options) => options.find(o => o.short === string || o.long === string)
@@ -1,39 +0,0 @@
1
- const findOption = require('./findOption'),
2
- findVariables = require('./findVariables')
3
-
4
- module.exports = (array, command) => {
5
- const options = command.optionsArray
6
- const result = {}
7
- while (array.length > 0 && array[0].startsWith('-')) {
8
- if (array[0].startsWith('--')) {
9
- let string = array.shift()
10
- string = string.replace('--', '')
11
- const option = findOption(string, options)
12
- if (!option) throw new Error(`Unknown Option: --${string}`)
13
- let vars
14
- try {
15
- vars = findVariables(option.long, array, option.variables, command.commandStrings)
16
- } catch(err) {
17
- err.message += ` for --${option.long}`
18
- throw err
19
- }
20
- Object.assign(result, vars)
21
- } else {
22
- let string = array.shift()
23
- const substring = string.slice(1, 2)
24
- const option = findOption(substring, options)
25
- if (!option) throw new Error(`Unknown Option: -${substring}`)
26
- string = string.replace(substring, '')
27
- if (string !== '-') array.unshift(string)
28
- let vars
29
- try {
30
- vars = findVariables(option.long, array, option.variables, command.commandStrings)
31
- } catch(err) {
32
- err.message += ` for --${option.long}`
33
- throw err
34
- }
35
- Object.assign(result, vars)
36
- }
37
- }
38
- return result
39
- }
@@ -1,15 +0,0 @@
1
- module.exports = (array, variable, commands) => {
2
- let result
3
- if (array.length > 0 && !array[0].startsWith('-') && !variable.array) {
4
- if ((!commands.includes(array[0]) || variable.required) && array[0] !== 'help') result = array.shift()
5
- } else if (array.length > 0 && variable.array) {
6
- result = []
7
- while(array.length > 0 && !array[0].startsWith('-')) {
8
- if (commands.includes(array[0])) break
9
- result.push(array.shift())
10
- }
11
- }
12
- if (!result && variable.required) throw new Error(`Missing required variable <${variable.name}>`)
13
- if (!result) result = true
14
- return result
15
- }
@@ -1,23 +0,0 @@
1
- const findVariable = require('./findVariable')
2
-
3
- module.exports = (base, array, variables, commands) => {
4
- let result = {}
5
- if (!variables) {
6
- if (base) result[base] = true
7
- return result
8
- }
9
- if (variables.length > 1) result[base] = {}
10
- for (let variable of variables) {
11
- let newVar = findVariable(array, variable, commands)
12
- if (variables.length > 1) {
13
- result[base][variable.name] = newVar
14
- } else {
15
- if (base) {
16
- result[base] = newVar
17
- } else {
18
- result[variable.name] = newVar
19
- }
20
- }
21
- }
22
- return result
23
- }
@@ -1,22 +0,0 @@
1
- let Variable = require('../models/Variable')
2
-
3
- module.exports = (string) => {
4
- let results = []
5
- let variables = string.split(' ')
6
- for (let variable of variables) {
7
- let raw = variable.trim()
8
- if (variable.startsWith('<') && variable.endsWith('>')) {
9
- variable = variable.replace('<', '').replace('>', '')
10
- results.push(new Variable({name: variable, raw, required: true}))
11
- } else if (variable.startsWith('[') && variable.endsWith('...]')) {
12
- variable = variable.replace('[', '').replace('...]', '')
13
- results.push(new Variable({array: true, name: variable, raw}))
14
- } else if (variable.startsWith('[') && variable.endsWith(']')) {
15
- variable = variable.replace('[', '').replace(']', '')
16
- results.push(new Variable({name: variable, raw}))
17
- } else {
18
- throw `Unrecognized variable description: ${variable}`
19
- }
20
- }
21
- return results
22
- }
@@ -1,17 +0,0 @@
1
- const findCommand = require('./findCommand'),
2
- findCommandVariables = require('./findCommandVariables'),
3
- findOption = require('./findOption'),
4
- findOptions = require('./findOptions'),
5
- findVariable = require('./findVariable'),
6
- findVariables = require('./findVariables'),
7
- getVariables = require('./getVariables')
8
-
9
- module.exports = {
10
- findCommand,
11
- findCommandVariables,
12
- findOption,
13
- findOptions,
14
- findVariable,
15
- findVariables,
16
- getVariables
17
- }
package/src/index.js DELETED
@@ -1,5 +0,0 @@
1
- const config = require('./config'),
2
- helpers = require('./helpers'),
3
- models = require('./models')
4
-
5
- module.exports = { config, helpers, models }