|
1 | 1 | /** |
2 | 2 | * Argument parsing utilities for CLI applications. |
3 | | - * Re-exports Node.js parseArgs with additional Socket-specific functionality. |
| 3 | + * Uses yargs-parser internally for robust argument parsing with Node.js parseArgs-compatible API. |
4 | 4 | */ |
5 | 5 |
|
6 | | -import { parseArgs as nodeParseArgs } from 'node:util' |
| 6 | +import yargsParser from '../external/yargs-parser' |
7 | 7 |
|
8 | | -export type { ParseArgsConfig } from 'node:util' |
9 | | -export { parseArgs } from 'node:util' |
| 8 | +/** |
| 9 | + * Yargs parser options interface. |
| 10 | + */ |
| 11 | +interface YargsOptions { |
| 12 | + // Array of option names that should be treated as booleans. |
| 13 | + boolean?: string[] | undefined |
| 14 | + // Array of option names that should be treated as strings. |
| 15 | + string?: string[] | undefined |
| 16 | + // Array of option names that should accept multiple values. |
| 17 | + array?: string[] | undefined |
| 18 | + // Map of short aliases to full option names. |
| 19 | + alias?: Record<string, string | string[]> | undefined |
| 20 | + // Default values for options. |
| 21 | + default?: Record<string, unknown> | undefined |
| 22 | + // Transform functions to coerce parsed values. |
| 23 | + coerce?: Record<string, (value: unknown) => unknown> | undefined |
| 24 | + // Whether to treat unknown options as positional arguments. |
| 25 | + 'unknown-options-as-args'?: boolean | undefined |
| 26 | + // Whether to parse numeric strings as numbers. |
| 27 | + 'parse-numbers'?: boolean | undefined |
| 28 | + // Whether to parse positional arguments as numbers. |
| 29 | + 'parse-positional-numbers'?: boolean | undefined |
| 30 | + // Whether to support --no-<option> negation for booleans. |
| 31 | + 'boolean-negation'?: boolean | undefined |
| 32 | + // Whether to stop parsing options after the first positional. |
| 33 | + 'halt-at-non-option'?: boolean | undefined |
| 34 | + // Advanced yargs-parser configuration options. |
| 35 | + configuration?: Record<string, boolean | string> | undefined |
| 36 | + // Whether to throw on unknown options. |
| 37 | + strict?: boolean | undefined |
| 38 | +} |
| 39 | + |
| 40 | +/** |
| 41 | + * Yargs parser result interface. |
| 42 | + */ |
| 43 | +interface YargsArguments extends Record<string, unknown> { |
| 44 | + _: string[] |
| 45 | + $0?: string |
| 46 | +} |
| 47 | + |
| 48 | +/** |
| 49 | + * Options for configuring argument parsing, similar to Node.js util.parseArgs. |
| 50 | + */ |
| 51 | +export interface ParseArgsOptionsConfig { |
| 52 | + // Whether the option accepts multiple values (array). |
| 53 | + multiple?: boolean | undefined |
| 54 | + // Short alias for the option (single character). |
| 55 | + short?: string | undefined |
| 56 | + // Type of the option value. |
| 57 | + type?: 'boolean' | 'string' | undefined |
| 58 | + // Default value for the option. |
| 59 | + default?: unknown | undefined |
| 60 | + // Transform function to coerce parsed values. |
| 61 | + coerce?: (value: unknown) => unknown | undefined |
| 62 | +} |
| 63 | + |
| 64 | +/** |
| 65 | + * Configuration object for parseArgs function, similar to Node.js util.parseArgs. |
| 66 | + */ |
| 67 | +export interface ParseArgsConfig { |
| 68 | + // Command-line arguments to parse (defaults to process.argv.slice(2)). |
| 69 | + args?: readonly string[] | undefined |
| 70 | + // Options configuration object. |
| 71 | + options?: Record<string, ParseArgsOptionsConfig> | undefined |
| 72 | + // Whether to throw on unknown options (default: true). |
| 73 | + strict?: boolean | undefined |
| 74 | + // Whether to populate tokens array (not implemented, for API compatibility). |
| 75 | + tokens?: boolean | undefined |
| 76 | + // Whether to allow positional arguments after options. |
| 77 | + allowPositionals?: boolean | undefined |
| 78 | + // Whether to allow negative numbers as option values. |
| 79 | + allowNegative?: boolean | undefined |
| 80 | + // Advanced yargs-parser configuration passthrough. |
| 81 | + configuration?: Record<string, boolean | string> | undefined |
| 82 | +} |
| 83 | + |
| 84 | +/** |
| 85 | + * Result of parsing command-line arguments. |
| 86 | + */ |
| 87 | +export interface ParsedArgs<T = Record<string, unknown>> { |
| 88 | + // Parsed option values. |
| 89 | + values: T |
| 90 | + // Positional arguments (non-option arguments). |
| 91 | + positionals: string[] |
| 92 | + // Raw parsed arguments object from yargs-parser. |
| 93 | + raw: YargsArguments |
| 94 | +} |
| 95 | + |
| 96 | +/** |
| 97 | + * Parse command-line arguments with a Node.js parseArgs-compatible API. |
| 98 | + * Uses yargs-parser internally for robust argument parsing. |
| 99 | + */ |
| 100 | +export function parseArgs<T = Record<string, unknown>>( |
| 101 | + config: ParseArgsConfig = {}, |
| 102 | +): ParsedArgs<T> { |
| 103 | + const { |
| 104 | + allowNegative = false, |
| 105 | + allowPositionals = true, |
| 106 | + args = process.argv.slice(2), |
| 107 | + configuration, |
| 108 | + options = {}, |
| 109 | + strict = true, |
| 110 | + } = config |
| 111 | + |
| 112 | + // Convert parseArgs options to yargs-parser options. |
| 113 | + const yargsOptions: YargsOptions = { |
| 114 | + // Arrays of option names to treat as specific types. |
| 115 | + boolean: [], |
| 116 | + string: [], |
| 117 | + array: [], |
| 118 | + // Maps for aliases, defaults, and transformations. |
| 119 | + alias: {}, |
| 120 | + default: {}, |
| 121 | + coerce: {}, |
| 122 | + 'unknown-options-as-args': !strict, |
| 123 | + 'parse-numbers': false, |
| 124 | + 'parse-positional-numbers': false, |
| 125 | + 'boolean-negation': !allowNegative, |
| 126 | + 'halt-at-non-option': !allowPositionals, |
| 127 | + configuration: { |
| 128 | + // Enable kebab-case to camelCase conversion (e.g., --temp-dir → tempDir). |
| 129 | + 'camel-case-expansion': true, |
| 130 | + // Disable dot notation to avoid confusing nested property parsing. |
| 131 | + 'dot-notation': false, |
| 132 | + // Convert duplicate arguments into arrays automatically. |
| 133 | + 'duplicate-arguments-array': true, |
| 134 | + // Flatten nested arrays from duplicate arguments for cleaner output. |
| 135 | + 'flatten-duplicate-arrays': true, |
| 136 | + // Populate the '--' key with arguments after the -- separator. |
| 137 | + 'populate--': true, |
| 138 | + // Allow short option grouping like -abc for -a -b -c. |
| 139 | + 'short-option-groups': true, |
| 140 | + // Keep aliased keys in the result for flexibility. |
| 141 | + 'strip-aliased': false, |
| 142 | + // Keep both kebab-case and camelCase keys for flexibility. |
| 143 | + 'strip-dashed': false, |
| 144 | + ...configuration, |
| 145 | + }, |
| 146 | + } |
| 147 | + |
| 148 | + // Process each option configuration. |
| 149 | + for (const { 0: key, 1: optionConfig } of Object.entries(options)) { |
| 150 | + const { |
| 151 | + coerce, |
| 152 | + default: defaultValue, |
| 153 | + multiple, |
| 154 | + short, |
| 155 | + type, |
| 156 | + } = optionConfig |
| 157 | + |
| 158 | + // Set the option type. |
| 159 | + if (type === 'boolean') { |
| 160 | + yargsOptions.boolean?.push(key) |
| 161 | + } else if (type === 'string') { |
| 162 | + yargsOptions.string?.push(key) |
| 163 | + } |
| 164 | + |
| 165 | + // Handle multiple values (arrays). |
| 166 | + if (multiple) { |
| 167 | + yargsOptions.array?.push(key) |
| 168 | + } |
| 169 | + |
| 170 | + // Set short alias. |
| 171 | + if (short) { |
| 172 | + ;(yargsOptions.alias as Record<string, string>)[short] = key |
| 173 | + } |
| 174 | + |
| 175 | + // Set default value. |
| 176 | + if (defaultValue !== undefined) { |
| 177 | + ;(yargsOptions.default as Record<string, unknown>)[key] = defaultValue |
| 178 | + } |
| 179 | + |
| 180 | + // Set coerce function. |
| 181 | + if (coerce) { |
| 182 | + ;(yargsOptions.coerce as Record<string, unknown>)[key] = coerce |
| 183 | + } |
| 184 | + } |
| 185 | + |
| 186 | + // Parse the arguments. |
| 187 | + const parsed = yargsParser(args as string[], yargsOptions) |
| 188 | + |
| 189 | + // Extract positional arguments. |
| 190 | + const positionals = parsed._ || [] |
| 191 | + |
| 192 | + // Remove the positionals array from values to match Node.js parseArgs behavior. |
| 193 | + const { _, ...values } = parsed |
| 194 | + |
| 195 | + // Ensure positionals are strings. |
| 196 | + const stringPositionals = positionals.map(String) |
| 197 | + |
| 198 | + return { |
| 199 | + values: values as T, |
| 200 | + positionals: stringPositionals, |
| 201 | + raw: parsed as YargsArguments, |
| 202 | + } |
| 203 | +} |
10 | 204 |
|
11 | 205 | /** |
12 | 206 | * Parse command-line arguments with Socket defaults. |
13 | 207 | * Provides sensible defaults for Socket CLI applications. |
14 | 208 | */ |
15 | | -export function parseArgsWithDefaults( |
16 | | - config?: Parameters<typeof nodeParseArgs>[0], |
17 | | -): ReturnType<typeof nodeParseArgs> { |
18 | | - return nodeParseArgs({ |
| 209 | +export function parseArgsWithDefaults<T = Record<string, unknown>>( |
| 210 | + config: ParseArgsConfig = {}, |
| 211 | +): ParsedArgs<T> { |
| 212 | + return parseArgs<T>({ |
19 | 213 | strict: false, |
20 | 214 | allowPositionals: true, |
21 | 215 | ...config, |
22 | 216 | }) |
23 | 217 | } |
24 | 218 |
|
| 219 | +/** |
| 220 | + * Common parseArgs configuration for Socket registry scripts. |
| 221 | + */ |
| 222 | +export const commonParseArgsConfig: ParseArgsConfig = { |
| 223 | + options: { |
| 224 | + force: { |
| 225 | + type: 'boolean', |
| 226 | + short: 'f', |
| 227 | + default: false, |
| 228 | + }, |
| 229 | + quiet: { |
| 230 | + type: 'boolean', |
| 231 | + short: 'q', |
| 232 | + default: false, |
| 233 | + }, |
| 234 | + }, |
| 235 | + strict: false, |
| 236 | +} |
| 237 | + |
25 | 238 | /** |
26 | 239 | * Extract positional arguments from process.argv. |
27 | 240 | * Useful for commands that accept file paths or other positional parameters. |
|
0 commit comments