|
1 |
| -# pargv-lite |
2 |
| - |
3 |
| -JSON configurable command-line options parser |
4 |
| - |
5 |
| -3.x Under developing... not tested yet |
6 |
| - |
7 |
| -```javascript |
8 |
| -/** |
9 |
| - * @typedef {string} VarKey Variable name |
10 |
| - * @typedef {string | boolean | string[]} VarVal Variable value |
11 |
| - * @typedef {string} OptStr Option string, e.g. '--', '-o', '--option' |
12 |
| - * @typedef {OptStr | null} OptDef Option definitions, e.g. '--', '-o', '--option', or `null` to refer to the variable name |
13 |
| - * @typedef {OptDef | OptDef[]} OptKit one or more option definitions |
14 |
| - * |
15 |
| - * @typedef {object} VarKit Variable configuration object |
16 |
| - * @property {VarVal} def Variable **def**inition & **def**ault value (pun intended) |
17 |
| - * @property {OptKit} [set] Array of options to set the variable value |
18 |
| - * @property {OptKit} [rst] Array of options to reset the variable value |
19 |
| - * |
20 |
| - * @typedef {OptKit} HaltKit Halt options, identical to `OptKit`, for now... |
21 |
| - * @typedef {{opt: OptStr, key: VarKey}} HaltRes |
22 |
| - * @typedef {Record<VarKey, VarKit | HaltKit>} KeyKitMap |
23 |
| - * @typedef {Record<VarKey, VarVal>} KeyValMap |
24 |
| - * |
25 |
| - * @callback CanQuit |
26 |
| - * @param {{msg: string, i: number, opt: OptStr, key?: VarKey, val?: VarVal }} err |
27 |
| - * @returns {boolean} Whether the parsing should continue (false) or quit (true) |
28 |
| - * @typedef {Record<OptStr, VarKey>} OptKeyMap internal type |
29 |
| - */ |
30 |
| -/** |
31 |
| - * Command line argument parser function |
32 |
| - * @param {string[]} argv Command line arguments array |
33 |
| - * @param {number} i Index of current argument being processed |
34 |
| - * @param {KeyKitMap} req Options structure definition |
35 |
| - * @param {KeyValMap} res Object to store parsed results |
36 |
| - * @param {CanQuit} err Error handler function |
37 |
| - * @returns {{ i: number, halt?: HaltRes }} |
38 |
| - * @example |
39 |
| - */ |
40 |
| -export default function parse(argv, i, req, res, err); |
41 |
| -``` |
| 1 | +# 🚀 pargv-lite |
| 2 | + |
| 3 | +A lightning-fast, single-pass command line argument parser with structural validation and elegant JSON configuration. |
| 4 | + |
| 5 | +## ✨ Features |
| 6 | + |
| 7 | +- **Concise JSON Configuration**: Define your entire command structure in a single JSON object |
| 8 | +- **Single-Pass Processing**: Parses, validates, and populates all in one efficient scan |
| 9 | +- **Type Safety**: Built-in structure and type validation during parsing |
| 10 | +- **Error Resilience**: Configurable error handling with continue/abort control |
| 11 | +- **Seamless Subcommand Support**: First-class "exit" mechanism for complex command structures |
| 12 | +- **Zero Dependencies**: Pure JavaScript with no external libraries |
| 13 | +- **Predictable Results**: No type errors or undefined values (all variables are initialized) |
| 14 | + |
| 15 | +## 📦 Installation |
| 16 | + |
| 17 | +```bash |
| 18 | +npm install pargv-lite |
| 19 | +``` |
| 20 | + |
| 21 | +## 🔍 Elegant Usage Example |
| 22 | + |
| 23 | +```javascript |
| 24 | +import parse from 'pargv-lite'; |
| 25 | + |
| 26 | +// Define your application's command structure |
| 27 | +const gitReq = { |
| 28 | + // Global options |
| 29 | + help: { |
| 30 | + def: false, |
| 31 | + set: ['-h', '--help'] |
| 32 | + }, |
| 33 | + verbose: { |
| 34 | + def: false, |
| 35 | + set: ['-v', '--verbose'] |
| 36 | + }, |
| 37 | + // Subcommands (direct string array format for exit options) |
| 38 | + command: ['clone', 'push', 'pull', 'commit', 'checkout'] |
| 39 | +}; |
| 40 | + |
| 41 | +// Commit subcommand config |
| 42 | +const commitReq = { |
| 43 | + message: { |
| 44 | + def: '', |
| 45 | + set: ['-m', '--message'] |
| 46 | + }, |
| 47 | + all: { |
| 48 | + def: false, |
| 49 | + set: ['-a', '--all'] |
| 50 | + }, |
| 51 | + amend: { |
| 52 | + def: false, |
| 53 | + set: ['--amend'] |
| 54 | + } |
| 55 | +}; |
| 56 | + |
| 57 | +function main() { |
| 58 | + const gitRes = {}; |
| 59 | + |
| 60 | + // First parse to handle global options and identify subcommand |
| 61 | + const ret = parse(process.argv, 2, gitReq, gitRes, errorHandler); |
| 62 | + |
| 63 | + // Handle help flag |
| 64 | + if (gitRes.help) { |
| 65 | + showHelp(); |
| 66 | + return; |
| 67 | + } |
| 68 | + |
| 69 | + // Check if a subcommand was encountered (returns object with next position) |
| 70 | + if (typeof ret === 'object') { |
| 71 | + // ret contains { i, key, opt }: |
| 72 | + // - ret.i is the index to continue parsing from (already incremented) |
| 73 | + // - ret.key is the variable name in gitReq that triggered the exit ('command') |
| 74 | + // - ret.opt is the option string that triggered the exit (subcommand name) |
| 75 | + |
| 76 | + console.log(`Executing ${ret.opt} command...`); |
| 77 | + |
| 78 | + switch (ret.opt) { |
| 79 | + case 'commit': |
| 80 | + // Parse commit-specific options starting from ret.i |
| 81 | + const commitRes = {}; |
| 82 | + parse(process.argv, ret.i, commitReq, commitRes, errorHandler); |
| 83 | + |
| 84 | + // Use the results |
| 85 | + console.log( |
| 86 | + `Committing with message: ${commitRes.message}`, |
| 87 | + commitRes.all ? '(all files)' : '', |
| 88 | + commitRes.amend ? '(amending)' : '' |
| 89 | + ); |
| 90 | + break; |
| 91 | + |
| 92 | + case 'push': |
| 93 | + // Handle push command... |
| 94 | + break; |
| 95 | + |
| 96 | + // Handle other commands... |
| 97 | + } |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +// Error handler |
| 102 | +function errorHandler(err) { |
| 103 | + console.error(`Error at arg ${err.i}: ${err.msg}`); |
| 104 | + return false; // Continue parsing |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +## 🛠️ API |
| 109 | + |
| 110 | +```javascript |
| 111 | +parse(argv, i, req, res, err) |
| 112 | +``` |
| 113 | + |
| 114 | +- **argv**: Array of command line arguments (usually `process.argv`) |
| 115 | +- **i**: Starting index for parsing (usually 2) |
| 116 | +- **req**: Configuration object defining your command structure |
| 117 | +- **res**: Object that will be populated with parsed results |
| 118 | +- **err**: Error handler function, receives `{msg, i, opt, key, val}` and returns boolean |
| 119 | + |
| 120 | +### Return Value |
| 121 | + |
| 122 | +The function returns either: |
| 123 | +- A number (the next index after parsing completed normally) |
| 124 | +- An object `{ i, key, opt }` when exiting early due to an exit option, where: |
| 125 | + - `i`: The next index to resume parsing from (already incremented past the exit option) |
| 126 | + - `key`: The variable name in the req object that triggered the exit |
| 127 | + - `opt`: The option string that triggered the exit (e.g., the subcommand name) |
| 128 | + |
| 129 | +### Configuration Format |
| 130 | + |
| 131 | +```javascript |
| 132 | +{ |
| 133 | + // Regular variable with default value and option definitions |
| 134 | + variableName: { |
| 135 | + def: defaultValue, // Required: Boolean, string, or string[] |
| 136 | + set: optionDefinition, // Options that set this variable |
| 137 | + rst: optionDefinition // Options that reset this variable to default |
| 138 | + }, |
| 139 | + |
| 140 | + // Exit option (shorthand format) - exits parsing when encountered |
| 141 | + exitOptionName: optionDefinition |
| 142 | +} |
| 143 | +``` |
| 144 | + |
| 145 | +Option definitions can be: |
| 146 | +- A string: `'--option'` |
| 147 | +- An array of strings: `['--option', '-o']` |
| 148 | +- Include `null` in an array to use the variable name as an option: `[null, '-o']` |
| 149 | + |
| 150 | +## ⚡ Powerful Features |
| 151 | + |
| 152 | +### Boolean Options |
| 153 | + |
| 154 | +```javascript |
| 155 | +// Simple flags (no value needed) |
| 156 | +verbose: { |
| 157 | + def: false, |
| 158 | + set: ['-v', '--verbose'] |
| 159 | +} |
| 160 | +``` |
| 161 | + |
| 162 | +### String Options |
| 163 | + |
| 164 | +```javascript |
| 165 | +// String option with default value |
| 166 | +output: { |
| 167 | + def: 'stdout', |
| 168 | + set: ['-o', '--output'] |
| 169 | +} |
| 170 | +``` |
| 171 | + |
| 172 | +### Array Collection |
| 173 | + |
| 174 | +```javascript |
| 175 | +// Collect multiple values in an array |
| 176 | +files: { |
| 177 | + def: [], |
| 178 | + set: ['-i', '--input'] // -i 1.txt --input 2.txt -i3.txt --input=4.txt |
| 179 | +} |
| 180 | + |
| 181 | +// '--' automatically collects all the anonymous arguments. |
| 182 | +// If you write it out, it collects all the rest arguments. |
| 183 | +allFiles: { |
| 184 | + def: [], |
| 185 | + set: ['--', '-i'] // -i 1.txt 2.txt -- --this-will-be-collected-too |
| 186 | +} |
| 187 | +``` |
| 188 | + |
| 189 | +### Reset Options |
| 190 | + |
| 191 | +```javascript |
| 192 | +// Option to reset value back to default |
| 193 | +// For boolean values: |
| 194 | +color: { |
| 195 | + def: false, |
| 196 | + set: ['--color'], // Sets to true (!def) |
| 197 | + rst: ['--no-color'] // Resets to false (def) |
| 198 | +} |
| 199 | + |
| 200 | +big: { |
| 201 | + def: true, |
| 202 | + set: ['--small'] // Sets to false (!def) |
| 203 | + rst: ['--big'], // Resets to true (def) |
| 204 | +} |
| 205 | + |
| 206 | +// For strings or arrays, reset restores the original default: |
| 207 | +files: { |
| 208 | + def: ['default.txt'], |
| 209 | + set: ['--files'], // Adds files to array |
| 210 | + rst: ['--no-files'] // Resets back to ['default.txt'] |
| 211 | +} |
| 212 | +``` |
| 213 | + |
| 214 | +Note: Inverting a boolean value is not supported now. |
| 215 | + |
| 216 | +### Combined Short Options |
| 217 | + |
| 218 | +```javascript |
| 219 | +// -abc is equivalent to -a -b -c |
| 220 | +const res = {}; |
| 221 | +parse(['node', 'app.js', '-abc'], 2, { |
| 222 | + a: { def: false, set: '-a' }, |
| 223 | + b: { def: false, set: '-b' }, |
| 224 | + c: { def: false, set: '-c' } |
| 225 | +}, res, errorHandler); |
| 226 | +// res = { a: true, b: true, c: true } |
| 227 | +``` |
| 228 | + |
| 229 | +## 🔄 Subcommand Handling |
| 230 | + |
| 231 | +The exit feature enables elegant subcommand handling: |
| 232 | + |
| 233 | +```javascript |
| 234 | +// Main CLI configuration |
| 235 | +const mainReq = { |
| 236 | + help: { |
| 237 | + def: false, |
| 238 | + set: ['-h', '--help'] |
| 239 | + }, |
| 240 | + // Direct array format for exit options |
| 241 | + command: ['build', 'serve', 'test'] |
| 242 | +}; |
| 243 | + |
| 244 | +const mainRes = {}; |
| 245 | +const ret = parse(process.argv, 2, mainReq, mainRes, errorHandler); |
| 246 | + |
| 247 | +if (typeof ret === 'object') { |
| 248 | + // When a command is found via the exit mechanism: |
| 249 | + // - ret.i is already positioned after the subcommand |
| 250 | + // - ret.key contains the variable name in req ('command' in this case) |
| 251 | + // - ret.opt contains the matched option (the subcommand name) |
| 252 | + |
| 253 | + switch(ret.opt) { |
| 254 | + case 'build': |
| 255 | + const buildReq = { /* build options */ }; |
| 256 | + const buildRes = {}; |
| 257 | + parse(process.argv, ret.i, buildReq, buildRes, errorHandler); |
| 258 | + break; |
| 259 | + } |
| 260 | +} |
| 261 | +``` |
| 262 | + |
| 263 | +## 📜 License |
| 264 | + |
| 265 | +MIT |
0 commit comments