(args: string[], spec: Record<string, FlagSpec>)
| 2219 | } |
| 2220 | |
| 2221 | function parseFlags(args: string[], spec: Record<string, FlagSpec>): ParseResult { |
| 2222 | const flags: Record<string, string | boolean> = {}; |
| 2223 | const positional: string[] = []; |
| 2224 | const aliasMap = new Map<string, string>(); |
| 2225 | for (const [name, s] of Object.entries(spec)) { |
| 2226 | if (s.alias) for (const a of s.alias) aliasMap.set(a, name); |
| 2227 | } |
| 2228 | |
| 2229 | let i = 0; |
| 2230 | while (i < args.length) { |
| 2231 | const arg = args[i]; |
| 2232 | if (arg === "--") { |
| 2233 | for (i++; i < args.length; i++) positional.push(args[i]); |
| 2234 | break; |
| 2235 | } |
| 2236 | if (arg.startsWith("--")) { |
| 2237 | const eq = arg.indexOf("="); |
| 2238 | const name = eq === -1 ? arg.slice(2) : arg.slice(2, eq); |
| 2239 | const inlineValue = eq === -1 ? undefined : arg.slice(eq + 1); |
| 2240 | const s = spec[name]; |
| 2241 | if (!s) return { error: `unknown option '--${name}'` }; |
| 2242 | if (s.kind === "bool") { |
| 2243 | if (inlineValue !== undefined) { |
| 2244 | return { error: `option '--${name}' does not take a value` }; |
| 2245 | } |
| 2246 | flags[name] = true; |
| 2247 | i++; |
| 2248 | continue; |
| 2249 | } |
| 2250 | if (s.kind === "value-or-bool") { |
| 2251 | // Bare `--flag` -> true; `--flag=x` -> x. The bare form |
| 2252 | // never consumes the next argv (it would be ambiguous |
| 2253 | // against a positional). |
| 2254 | flags[name] = inlineValue ?? true; |
| 2255 | i++; |
| 2256 | continue; |
| 2257 | } |
| 2258 | if (inlineValue !== undefined) { |
| 2259 | flags[name] = inlineValue; |
| 2260 | i++; |
| 2261 | continue; |
| 2262 | } |
| 2263 | const next = args[i + 1]; |
| 2264 | if (next === undefined) { |
| 2265 | return { error: `option '--${name}' requires a value` }; |
| 2266 | } |
| 2267 | flags[name] = next; |
| 2268 | i += 2; |
| 2269 | continue; |
| 2270 | } |
| 2271 | if (arg.startsWith("-") && arg.length > 1) { |
| 2272 | const short = arg.slice(1); |
| 2273 | // Look up by alias first; fall back to a spec entry whose |
| 2274 | // *name* is a single char matching the short form (so a |
| 2275 | // spec like `{ n: { kind: "value" } }` accepts `-n` without |
| 2276 | // declaring `alias: ["n"]`). |
| 2277 | const name = aliasMap.get(short) ?? (spec[short] ? short : undefined); |
| 2278 | if (!name) return { error: `unknown option '-${short}'` }; |
no test coverage detected