Skip to content

Commit e089464

Browse files
committed
Consolidate parseArgs into argv/parse
Move yargs-based parseArgs implementation from parse-args.ts to argv/parse.ts to consolidate all argument parsing functionality in one location. Update package.json exports to map parse-args to argv/parse for backwards compatibility.
1 parent 1c1bf66 commit e089464

File tree

3 files changed

+223
-227
lines changed

3 files changed

+223
-227
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -525,8 +525,8 @@
525525
"default": "./dist/packages/validation.js"
526526
},
527527
"./parse-args": {
528-
"types": "./dist/parse-args.d.ts",
529-
"default": "./dist/parse-args.js"
528+
"types": "./dist/argv/parse.d.ts",
529+
"default": "./dist/argv/parse.js"
530530
},
531531
"./path": {
532532
"types": "./dist/path.d.ts",

src/argv/parse.ts

Lines changed: 221 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,240 @@
11
/**
22
* 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.
44
*/
55

6-
import { parseArgs as nodeParseArgs } from 'node:util'
6+
import yargsParser from '../external/yargs-parser'
77

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+
}
10204

11205
/**
12206
* Parse command-line arguments with Socket defaults.
13207
* Provides sensible defaults for Socket CLI applications.
14208
*/
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>({
19213
strict: false,
20214
allowPositionals: true,
21215
...config,
22216
})
23217
}
24218

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+
25238
/**
26239
* Extract positional arguments from process.argv.
27240
* Useful for commands that accept file paths or other positional parameters.

0 commit comments

Comments
 (0)