fix(smartcli.helpers): Improve CLI argument parsing and Deno runtime detection; use getUserArgs consistently

This commit is contained in:
2025-10-28 14:59:46 +00:00
parent e905af4b21
commit 913f8556d0
5 changed files with 55 additions and 28 deletions

View File

@@ -1,28 +1,42 @@
## Cross-Runtime Compatibility
### CLI Argument Parsing
The module uses a robust cross-runtime approach for parsing command-line arguments:
The module uses a robust cross-runtime approach for parsing command-line arguments through the `getUserArgs()` utility in `ts/smartcli.helpers.ts`.
**Key Implementation:**
- `getUserArgs()` utility (in `ts/smartcli.helpers.ts`) handles process.argv differences across Node.js, Deno, and Bun
- Uses `process.execPath` basename detection instead of content-based heuristics
- Prefers `Deno.args` when available (for Deno run/compiled), unless argv is explicitly provided
**Runtime-Specific Implementations:**
**Runtime Differences:**
- **Node.js**: `process.argv = ["/path/to/node", "/path/to/script.js", ...userArgs]`
- **Deno (run)**: `process.argv = ["deno", "/path/to/script.ts", ...userArgs]` (but `Deno.args` is preferred)
- **Deno (compiled)**: `process.argv = ["/path/to/executable", ...userArgs]` (custom executable name)
- **Bun**: `process.argv = ["/path/to/bun", "/path/to/script.ts", ...userArgs]`
| Runtime | process.argv Structure | Preferred API | Reason |
|---------|------------------------|---------------|---------|
| **Node.js** | `["/path/to/node", "/path/to/script.js", ...userArgs]` | Manual parsing | No native user-args API |
| **Deno run** | `["deno", "/path/to/script.ts", ...userArgs]` | `Deno.args` ✅ | Pre-filtered by runtime |
| **Deno compiled** | `["/path/to/binary", "/tmp/deno-compile-.../mod.ts", ...userArgs]` | `Deno.args` ✅ | Filters internal bundle path |
| **Bun** | `["/path/to/bun", "/path/to/script.ts", ...userArgs]` | Manual parsing | Bun.argv not pre-filtered |
**How it works:**
1. If `Deno.args` exists and no custom argv provided, use it directly
2. Otherwise, detect runtime by checking `process.execPath` basename
3. If basename is a known launcher (node, deno, bun, tsx, ts-node), skip 2 args
4. If basename is unknown (compiled executable), skip only 1 arg
5. Safety check: if offset would skip everything, don't skip anything (handles test edge cases)
**Why Deno.args is Critical for Compiled Executables:**
This approach works correctly with:
- Standard runtime execution
- Compiled executables (Deno compile, Node pkg, etc.)
- Custom-named executables
- Test environments with unusual argv setups
Deno compiled executables insert an internal bundle path at `argv[1]`:
```javascript
process.argv = [
"/usr/local/bin/moxytool", // argv[0] - executable
"/tmp/deno-compile-moxytool/mod.ts", // argv[1] - INTERNAL bundle path
"scripts", // argv[2] - actual user command
"--option" // argv[3+] - user args
]
Deno.args = ["scripts", "--option"] // ✓ Correctly filtered by Deno runtime
```
**getUserArgs() Logic:**
1. **Prefer Deno.args** when available (unless process.argv appears manipulated for testing)
2. **Fallback to manual parsing** for Node.js and Bun:
- Check `process.execPath` basename
- Known launchers (node, deno, bun, tsx, ts-node) → skip 2 args
- Unknown (compiled executables) → skip 1 arg
3. **Test detection**: If `process.argv.length > 2` in Deno, use manual parsing (handles test manipulation)
**Key Benefits:**
- ✅ Works with custom-named compiled executables
- ✅ Handles Deno's internal bundle path automatically
- ✅ Compatible with test environments
- ✅ No heuristics needed for Deno (runtime does the work)