feat: type: 'number' flags, long-form aliases, and unparseFlags()#38
Draft
voxpelli wants to merge 4 commits into
Draft
feat: type: 'number' flags, long-form aliases, and unparseFlags()#38voxpelli wants to merge 4 commits into
voxpelli wants to merge 4 commits into
Conversation
peowly only supported 'string' and 'boolean' (the two types util.parseArgs() natively understands). Consumers like Mocha need numeric flags for --retries and --jobs without manual Number() coercion in application code. A pre-parseArgs step remaps number flags to type:'string', and a post-parseArgs coercion loop converts parsed string values back to numbers. Numeric defaults are tracked separately and applied after coercion. Note: negative numbers require '--flag=-N' syntax due to a parseArgs limitation (it treats '--flag -N' as ambiguous with short flag clusters). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Allows declaring long-form aliases (e.g. --colors → --color) via an `aliases` property on any flag definition. Aliases are rewritten in argv before parseArgs sees them, supporting plain, =value, and multiple alias forms. Opt-in alias display in help via `showAliasInHelp: true`. Collision detection throws on duplicate or conflicting alias names. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Useful for rebuilding a CLI argv for child_process.spawn(), e.g. in parallel test runners. Boolean true emits --flag, false is omitted, strings/numbers emit --flag value, multiples emit one pair per element. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
index.js is auto-detected from package.json exports, so the explicit entry was redundant. jq is used in test:tstyche to read the minimum TypeScript version but is a system tool rather than an npm package. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
voxpelli
force-pushed
the
feat/number-aliases-unparse
branch
from
March 27, 2026 16:26
7b3bf59 to
7ef8b55
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds richer flag declaration capabilities to peowly (built on Node’s util.parseArgs()), aimed at Mocha-like CLIs needing numeric flags, long-form aliases, and the ability to serialize parsed flags back into an argv array.
Changes:
- Add
type: 'number'support by remapping number flags to parse as strings and coercing back to numbers (with NaN validation and default/multiple support). - Add
aliases: string[]for long-form flag aliases (with optional help annotations and collision detection) by rewriting argv before parsing. - Add
unparseFlags(flags, flagDefs) => string[]to serialize a flags object back into argv (plus tests/type-tests).
Reviewed changes
Copilot reviewed 11 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/peowly.js | Implements number-flag remapping/coercion and argv alias rewriting prior to parseArgs(). |
| lib/flag-types.d.ts | Extends flag typings to include type: 'number', aliases, and showAliasInHelp; introduces internal ParseArgsCompatibleFlag. |
| lib/peowly-types.d.ts | Updates TypedFlags typing to support numeric flags; adjusts ExtendedParseArgsConfig to omit options. |
| lib/format-lists.js | Displays numeric defaults and optionally annotates aliases in help output. |
| lib/unparse.js | Adds unparseFlags() implementation with documented serialization rules. |
| lib/main.js | Re-exports unparseFlags from the public entrypoint. |
| test/peowly-number-flags.spec.js | Runtime coverage for parsing/coercing numeric flags (including defaults and multiple). |
| test/peowly-aliases.spec.js | Runtime coverage for alias rewriting, collision detection, and help annotation behavior. |
| test/peowly-unparse.spec.js | Runtime coverage for unparseFlags output and basic round-trip cases. |
| typetests/number-flags.tst.ts | Type-level assertions for inferred numeric flag result types and assignability. |
| typetests/aliases.tst.ts | Type-level assertions for aliases/showAliasInHelp in flag definitions. |
| typetests/unparse.tst.ts | Type-level assertions for unparseFlags return type and compatibility with peowly() output. |
| .knip.jsonc | Knip config cleanup and ignores (jq binary, typetests). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for (const [canonicalName, flag] of Object.entries(resolvedFlags)) { | ||
| for (const alias of flag.aliases ?? []) { | ||
| if (resolvedFlags[alias] !== undefined) { | ||
| throw new Error(`Flag alias "${alias}" conflicts with existing flag "${canonicalName}"`); |
Comment on lines
+109
to
+129
| : args.map(arg => { | ||
| if (!arg.startsWith('--')) return arg; | ||
| const eqIdx = arg.indexOf('='); | ||
| if (eqIdx !== -1) { | ||
| const alias = arg.slice(2, eqIdx); | ||
| const canonical = aliasMap.get(alias); | ||
| return canonical ? `--${canonical}=${arg.slice(eqIdx + 1)}` : arg; | ||
| } | ||
| const flagName = arg.slice(2); | ||
| const canonical = aliasMap.get(flagName); | ||
| if (canonical) return `--${canonical}`; | ||
| if (flagName.startsWith('no-')) { | ||
| const aliasedName = flagName.slice(3); | ||
| const canonicalForNo = aliasMap.get(aliasedName); | ||
| if (canonicalForNo && resolvedFlags[canonicalForNo]?.type === 'boolean') { | ||
| return `--no-${canonicalForNo}`; | ||
| } | ||
| } | ||
| return arg; | ||
| }); | ||
|
|
Comment on lines
+5
to
+8
| * Rules: | ||
| * - `boolean true` → `['--name']` | ||
| * - `boolean false` → `[]` (omitted; `--no-name` is not emitted) | ||
| * - `string`/`number` → `['--name', String(value)]` |
|
|
||
| for (const [canonicalName, flag] of Object.entries(resolvedFlags)) { | ||
| for (const alias of flag.aliases ?? []) { | ||
| if (resolvedFlags[alias] !== undefined) { |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three related features for Mocha and similar CLI tools that need richer flag declarations than
util.parseArgs()exposes:type: 'number'flag support — declare numeric flags directly; peowly remaps them totype: 'string'beforeparseArgsand coerces back afterwards, with NaN validation and fulldefault/multiplesupportaliases: string[]— long-form argv aliases (e.g.--colors → --color) rewritten before parsing; supports plain,=value, and multiple-alias forms; opt-in help annotation viashowAliasInHelp: true; collision detection throws on duplicate or conflicting namesunparseFlags(flags, flagDefs) → string[]— serialises a parsed flags object back to an argv array forchild_process.spawn(), e.g. in parallel test runnersTest plan
npm testpasses (96 runtime tests, 108 type tests across TS 5.8 / 5.9 / 6.0, type coverage 99.87%)npm run check:knip— cleantype: 'number'round-trips: parse → flags.count isnumber, notstring--colorsresolves toflags.colorunparseFlagsround-trip:peowly(args).flags → unparseFlags → peowlygives identical flags🤖 Generated with Claude Code