UNPKG

args-tokens

Version:

parseArgs tokens compatibility and more high-performance parser

199 lines (198 loc) 5.01 kB
//#region src/parser.ts const HYPHEN_CHAR = "-"; const HYPHEN_CODE = HYPHEN_CHAR.codePointAt(0); const EQUAL_CHAR = "="; const EQUAL_CODE = EQUAL_CHAR.codePointAt(0); const TERMINATOR = "--"; const SHORT_OPTION_PREFIX = HYPHEN_CHAR; const LONG_OPTION_PREFIX = "--"; /** * Parse command line arguments. * * @param args - command line arguments * @param options - parse options, about details see {@link ParserOptions} * @returns Argument tokens. * * @example * ```js * import { parseArgs } from 'args-tokens' // for Node.js and Bun * // import { parseArgs } from 'jsr:@kazupon/args-tokens' // for Deno * * const tokens = parseArgs(['--foo', 'bar', '-x', '--bar=baz']) * // do something with using tokens * // ... * console.log('tokens:', tokens) * ``` */ function parseArgs(args, options = {}) { const { allowCompatible = false } = options; const tokens = []; const remainings = [...args]; let index = -1; let groupCount = 0; let hasShortValueSeparator = false; while (remainings.length > 0) { const arg = remainings.shift(); if (arg == void 0) break; const nextArg = remainings[0]; if (groupCount > 0) groupCount--; else index++; if (arg === TERMINATOR) { tokens.push({ kind: "option-terminator", index }); const mapped = remainings.map((arg$1) => { return { kind: "positional", index: ++index, value: arg$1 }; }); tokens.push(...mapped); break; } if (isShortOption(arg)) { const shortOption = arg.charAt(1); let value; let inlineValue; if (groupCount) { tokens.push({ kind: "option", name: shortOption, rawName: arg, index, value, inlineValue }); if (groupCount === 1 && hasOptionValue(nextArg)) { value = remainings.shift(); if (hasShortValueSeparator) { inlineValue = true; hasShortValueSeparator = false; } tokens.push({ kind: "option", index, value, inlineValue }); } } else tokens.push({ kind: "option", name: shortOption, rawName: arg, index, value, inlineValue }); if (value != null) ++index; continue; } if (isShortOptionGroup(arg)) { const expanded = []; let shortValue = ""; for (let i = 1; i < arg.length; i++) { const shortableOption = arg.charAt(i); if (hasShortValueSeparator) shortValue += shortableOption; else if (!allowCompatible && shortableOption.codePointAt(0) === EQUAL_CODE) hasShortValueSeparator = true; else expanded.push(`${SHORT_OPTION_PREFIX}${shortableOption}`); } if (shortValue) expanded.push(shortValue); remainings.unshift(...expanded); groupCount = expanded.length; continue; } if (isLongOption(arg)) { const longOption = arg.slice(2); tokens.push({ kind: "option", name: longOption, rawName: arg, index, value: void 0, inlineValue: void 0 }); continue; } if (isLongOptionAndValue(arg)) { const equalIndex = arg.indexOf(EQUAL_CHAR); const longOption = arg.slice(2, equalIndex); const value = arg.slice(equalIndex + 1); tokens.push({ kind: "option", name: longOption, rawName: `${LONG_OPTION_PREFIX}${longOption}`, index, value, inlineValue: true }); continue; } tokens.push({ kind: "positional", index, value: arg }); } return tokens; } /** * Check if `arg` is a short option (e.g. `-f`). * * @param arg - An argument to check * @returns Whether `arg` is a short option. */ function isShortOption(arg) { return arg.length === 2 && arg.codePointAt(0) === HYPHEN_CODE && arg.codePointAt(1) !== HYPHEN_CODE; } /** * Check if `arg` is a short option group (e.g. `-abc`). * * @param arg - An argument to check * @returns Whether `arg` is a short option group. */ function isShortOptionGroup(arg) { if (arg.length <= 2) return false; if (arg.codePointAt(0) !== HYPHEN_CODE) return false; if (arg.codePointAt(1) === HYPHEN_CODE) return false; return true; } /** * Check if `arg` is a long option (e.g. `--foo`). * * @param arg - An argument to check * @returns Whether `arg` is a long option. */ function isLongOption(arg) { return hasLongOptionPrefix(arg) && !arg.includes(EQUAL_CHAR, 3); } /** * Check if `arg` is a long option with value (e.g. `--foo=bar`). * * @param arg - An argument to check * @returns Whether `arg` is a long option. */ function isLongOptionAndValue(arg) { return hasLongOptionPrefix(arg) && arg.includes(EQUAL_CHAR, 3); } /** * Check if `arg` is a long option prefix (e.g. `--`). * * @param arg - An argument to check * @returns Whether `arg` is a long option prefix. */ function hasLongOptionPrefix(arg) { return arg.length > 2 && ~arg.indexOf(LONG_OPTION_PREFIX); } /** * Check if a `value` is an option value. * * @param value - A value to check * @returns Whether a `value` is an option value. */ function hasOptionValue(value) { return !(value == null) && value.codePointAt(0) !== HYPHEN_CODE; } //#endregion export { hasLongOptionPrefix, isShortOption, parseArgs };