( root: CommandNode, tokens: string[], ctx: CommandContext, )
| 218 | * whether the invocation counts as a "first use" for the welcome marker. |
| 219 | */ |
| 220 | export async function dispatch( |
| 221 | root: CommandNode, |
| 222 | tokens: string[], |
| 223 | ctx: CommandContext, |
| 224 | ): Promise<DispatchOutcome> { |
| 225 | const result = resolveCommand(root, ctx.contextPath ?? [], tokens); |
| 226 | |
| 227 | switch (result.kind) { |
| 228 | case "noop": |
| 229 | return { kind: "noop" }; |
| 230 | |
| 231 | case "meta": |
| 232 | await runMeta(result.name, result.args, root, ctx); |
| 233 | return { kind: "meta" }; |
| 234 | |
| 235 | case "run": { |
| 236 | // Help is only triggered when `help` / `--help` / `-h` sits IMMEDIATELY |
| 237 | // after the matched path. Later positions are arguments or flag values |
| 238 | // and must reach the handler unchanged (e.g. `config set trials --help` |
| 239 | // must surface a parse error, not silently print help). |
| 240 | const first = result.args[0]; |
| 241 | const wantsHelp = |
| 242 | first === "help" || first === "--help" || first === "-h"; |
| 243 | if (wantsHelp && result.node.printHelp) { |
| 244 | await result.node.printHelp(result.absolutePath); |
| 245 | return { kind: "help" }; |
| 246 | } |
| 247 | |
| 248 | if (result.node.handler) { |
| 249 | await result.node.handler(result.args, ctx); |
| 250 | } else if (result.args.length > 0) { |
| 251 | throw new Error( |
| 252 | `Unknown subcommand "${result.args[0]}" in ${pretty( |
| 253 | result.absolutePath, |
| 254 | )}`, |
| 255 | ); |
| 256 | } else if (result.node.printHelp) { |
| 257 | await result.node.printHelp(result.absolutePath); |
| 258 | return { kind: "help" }; |
| 259 | } |
| 260 | |
| 261 | // Descend on bare (REPL only). `config` and `config core` already |
| 262 | // printed via their handler; `experiments` printed via printHelp. |
| 263 | if ( |
| 264 | ctx.contextPath !== null && |
| 265 | result.node.children && |
| 266 | result.args.length === 0 |
| 267 | ) { |
| 268 | const target = result.absolutePath; |
| 269 | const same = |
| 270 | target.length === ctx.contextPath.length && |
| 271 | target.every((s, i) => s === ctx.contextPath![i]); |
| 272 | if (!same) { |
| 273 | ctx.setContextPath?.(target); |
| 274 | } |
| 275 | } |
| 276 | return { kind: "ran", absolutePath: result.absolutePath }; |
| 277 | } |
no test coverage detected