Skip to content

Commit 8ba0a55

Browse files
boneskullclaude
andcommitted
refactor!: remove pipe() function
BREAKING CHANGE: The `pipe()` function has been removed from the public API. The direct API patterns provide better type inference: - Parser merging: `positionals(options)` instead of `pipe(options, positionals)` - Transforms: `map(parser, fn)` instead of `pipe(parser, map(fn))` - Commands: `.command(name, parser, handler)` instead of `pipe(parser, handle(fn))` 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent d3688c8 commit 8ba0a55

7 files changed

Lines changed: 116 additions & 231 deletions

File tree

README.md

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -545,12 +545,7 @@ const plain = stripAnsi('\x1b[32m--verbose\x1b[0m'); // '--verbose'
545545

546546
### Low-Level Utilities
547547

548-
The `pipe()` and `handle()` functions are exported for advanced use cases, but they're rarely needed with the standard API:
549-
550-
- **`pipe(a, b, c)`** - General function composition. Mostly superseded by direct parser merging (`positionals(options)`) and `map(parser, fn)`.
551-
- **`handle(parser, fn)`** - Creates a `Command` from a parser and handler. Mostly superseded by `.command(name, parser, handler)`.
552-
553-
These exist for edge cases where you need to compose functions outside the fluent builder, but the main API covers most use cases with better type inference.
548+
The `handle(parser, fn)` function is exported for advanced use cases where you need to create a `Command` object outside the fluent builder. It's mostly superseded by `.command(name, parser, handler)`.
554549

555550
## Motivation
556551

src/bargs.ts

Lines changed: 9 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { parseSimple } from './parser.js';
2222
import { defaultTheme, getTheme, type Theme } from './theme.js';
2323

2424
// ═══════════════════════════════════════════════════════════════════════════════
25-
// PIPE COMBINATOR
25+
// INTERNAL TYPES
2626
// ═══════════════════════════════════════════════════════════════════════════════
2727

2828
// Type for commands that may have transforms
@@ -166,55 +166,6 @@ export function map<
166166
} as Parser<V2, P2> & { __transform: typeof fn };
167167
};
168168
}
169-
/**
170-
* Compose functions left-to-right.
171-
*/
172-
export function pipe<A, B>(a: A, ab: (a: A) => B): B;
173-
export function pipe<A, B, C>(a: A, ab: (a: A) => B, bc: (b: B) => C): C;
174-
175-
// ═══════════════════════════════════════════════════════════════════════════════
176-
// HANDLE COMBINATOR (TERMINAL)
177-
// ═══════════════════════════════════════════════════════════════════════════════
178-
179-
export function pipe<A, B, C, D>(
180-
a: A,
181-
ab: (a: A) => B,
182-
bc: (b: B) => C,
183-
cd: (c: C) => D,
184-
): D;
185-
186-
export function pipe<A, B, C, D, E>(
187-
a: A,
188-
ab: (a: A) => B,
189-
bc: (b: B) => C,
190-
cd: (c: C) => D,
191-
de: (d: D) => E,
192-
): E;
193-
194-
export function pipe<A, B, C, D, E, F>(
195-
a: A,
196-
ab: (a: A) => B,
197-
bc: (b: B) => C,
198-
cd: (c: C) => D,
199-
de: (d: D) => E,
200-
ef: (e: E) => F,
201-
): F;
202-
export function pipe<A, B, C, D, E, F, G>(
203-
a: A,
204-
ab: (a: A) => B,
205-
bc: (b: B) => C,
206-
cd: (c: C) => D,
207-
de: (d: D) => E,
208-
ef: (e: E) => F,
209-
fg: (f: F) => G,
210-
): G;
211-
export function pipe(
212-
initial: unknown,
213-
...fns: Array<(arg: unknown) => unknown>
214-
): unknown {
215-
return fns.reduce((acc, fn) => fn(acc), initial);
216-
}
217-
218169
// ═══════════════════════════════════════════════════════════════════════════════
219170
// CLI BUILDER
220171
// ═══════════════════════════════════════════════════════════════════════════════
@@ -225,27 +176,20 @@ export function pipe(
225176
* @example
226177
*
227178
* ```typescript
228-
* const cli = bargs
179+
* const cli = await bargs
229180
* .create('my-app', { version: '1.0.0' })
230181
* .globals(
231-
* pipe(
232-
* opt.options({ verbose: opt.boolean() }),
233-
* map(({ values }) => ({
234-
* values: { ...values, ts: Date.now() },
235-
* positionals: [] as const,
236-
* })),
237-
* ),
182+
* map(opt.options({ verbose: opt.boolean() }), ({ values }) => ({
183+
* values: { ...values, ts: Date.now() },
184+
* positionals: [] as const,
185+
* })),
238186
* )
239187
* .command(
240188
* 'greet',
241-
* pipe(
242-
* pos.positionals(pos.string({ name: 'name', required: true })),
243-
* handle(({ positionals }) =>
244-
* console.log(`Hello, ${positionals[0]}!`),
245-
* ),
246-
* ),
189+
* pos.positionals(pos.string({ name: 'name', required: true })),
190+
* ({ positionals }) => console.log(`Hello, ${positionals[0]}!`),
247191
* )
248-
* .run();
192+
* .parseAsync();
249193
* ```
250194
*/
251195
const create = (

src/index.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,20 @@
11
/**
22
* Main entry point for the bargs CLI argument parser.
33
*
4-
* Provides a parser combinator-inspired API for building type-safe CLIs.
4+
* Provides a combinator-style API for building type-safe CLIs.
55
*
66
* @example
77
*
88
* ```typescript
9-
* import { bargs, opt, pos, pipe, map, handle } from '@boneskull/bargs';
9+
* import { bargs, opt, pos } from '@boneskull/bargs';
1010
*
11-
* const cli = bargs
11+
* await bargs
1212
* .create('my-app', { version: '1.0.0' })
1313
* .globals(opt.options({ verbose: opt.boolean({ aliases: ['v'] }) }))
1414
* .command(
1515
* 'greet',
16-
* pipe(
17-
* pos.positionals(pos.string({ name: 'name', required: true })),
18-
* handle(({ positionals }) =>
19-
* console.log(`Hello, ${positionals[0]}!`),
20-
* ),
21-
* ),
16+
* pos.positionals(pos.string({ name: 'name', required: true })),
17+
* ({ positionals }) => console.log(`Hello, ${positionals[0]}!`),
2218
* 'Say hello',
2319
* )
2420
* .parseAsync();
@@ -28,7 +24,7 @@
2824
*/
2925

3026
// Main API
31-
export { bargs, handle, map, pipe } from './bargs.js';
27+
export { bargs, handle, map } from './bargs.js';
3228

3329
// Errors
3430
export { BargsError, HelpError, ValidationError } from './errors.js';

src/opt.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,11 @@ type CallableOptionsParser<V> = (<V2, P2 extends readonly unknown[]>(
6969
* Create a Parser from an options schema that can also merge with existing
7070
* parsers.
7171
*
72-
* Supports two usage patterns in pipe():
72+
* Supports two usage patterns:
7373
*
74-
* 1. As first arg: `pipe(opt.options(...), ...)` - used as Parser directly
75-
* 2. As later arg: `pipe(pos.positionals(...), opt.options(...), ...)` - called to
76-
* merge
74+
* 1. Standalone: `opt.options({ ... })` - returns a Parser
75+
* 2. Merging: `pos.positionals(...)(opt.options(...))` - merges positionals into
76+
* options
7777
*/
7878
const optionsImpl = <T extends OptionsSchema>(
7979
schema: T,
@@ -310,11 +310,11 @@ type EmptyObject = {};
310310
* Create a Parser from positional definitions that can also merge with existing
311311
* parsers.
312312
*
313-
* Supports two usage patterns in pipe():
313+
* Supports two usage patterns:
314314
*
315-
* 1. As first arg: `pipe(pos.positionals(...), ...)` - used as Parser directly
316-
* 2. As later arg: `pipe(opt.options(...), pos.positionals(...), ...)` - called to
317-
* merge
315+
* 1. Standalone: `pos.positionals(...)` - returns a Parser
316+
* 2. Merging: `pos.positionals(...)(opt.options(...))` - merges positionals into
317+
* options
318318
*/
319319
const positionalsImpl = <T extends PositionalsSchema>(
320320
...positionals: T

test/bargs.test.ts

Lines changed: 45 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { describe, it } from 'node:test';
66

77
import type { StringOption } from '../src/types.js';
88

9-
import { bargs, handle, map, pipe } from '../src/bargs.js';
9+
import { bargs, handle, map } from '../src/bargs.js';
1010
import { opt, pos } from '../src/opt.js';
1111

1212
describe('bargs.create()', () => {
@@ -52,10 +52,7 @@ describe('.command()', () => {
5252
it('registers a command', () => {
5353
const cli = bargs.create('test-cli').command(
5454
'greet',
55-
pipe(
56-
opt.options({ name: opt.string({ default: 'world' }) }),
57-
handle(() => {}),
58-
),
55+
handle(opt.options({ name: opt.string({ default: 'world' }) }), () => {}),
5956
);
6057

6158
expect(cli, 'to be defined');
@@ -64,10 +61,7 @@ describe('.command()', () => {
6461
it('accepts description as third argument', () => {
6562
const cli = bargs.create('test-cli').command(
6663
'greet',
67-
pipe(
68-
opt.options({}),
69-
handle(() => {}),
70-
),
64+
handle(opt.options({}), () => {}),
7165
'Greet someone',
7266
);
7367

@@ -139,13 +133,11 @@ describe('.parseAsync()', () => {
139133

140134
const cli = bargs.create('test-cli').command(
141135
'greet',
142-
pipe(
143-
opt.options({ name: opt.string({ default: 'world' }) }),
144-
handle(({ values }) => {
145-
handlerCalled = true;
146-
handlerResult = values;
147-
}),
148-
),
136+
opt.options({ name: opt.string({ default: 'world' }) }),
137+
({ values }) => {
138+
handlerCalled = true;
139+
handlerResult = values;
140+
},
149141
'Greet someone',
150142
);
151143

@@ -163,12 +155,10 @@ describe('.parseAsync()', () => {
163155
.globals(opt.options({ verbose: opt.boolean({ default: false }) }))
164156
.command(
165157
'greet',
166-
pipe(
167-
opt.options({ name: opt.string({ default: 'world' }) }),
168-
handle(({ values }) => {
169-
handlerResult = values;
170-
}),
171-
),
158+
opt.options({ name: opt.string({ default: 'world' }) }),
159+
({ values }) => {
160+
handlerResult = values;
161+
},
172162
);
173163

174164
await cli.parseAsync(['greet', '--verbose', '--name', 'Bob']);
@@ -211,13 +201,13 @@ describe('.parseAsync()', () => {
211201
});
212202

213203
it('returns parsed result with command name', async () => {
214-
const cli = bargs.create('test-cli').command(
215-
'greet',
216-
pipe(
204+
const cli = bargs
205+
.create('test-cli')
206+
.command(
207+
'greet',
217208
opt.options({ name: opt.string({ default: 'world' }) }),
218-
handle(() => {}),
219-
),
220-
);
209+
() => {},
210+
);
221211

222212
const result = await cli.parseAsync(['greet', '--name', 'Test']);
223213

@@ -233,20 +223,17 @@ describe('transforms via map()', () => {
233223
const cli = bargs
234224
.create('test-cli')
235225
.globals(
236-
pipe(
226+
map(
237227
opt.options({ name: opt.string({ default: 'world' }) }),
238-
map(({ values }) => ({
228+
({ values }) => ({
239229
positionals: [] as const,
240230
values: { ...values, greeting: `Hello, ${values.name}!` },
241-
})),
231+
}),
242232
),
243233
)
244-
.command(
245-
'greet',
246-
handle(opt.options({}), ({ values }) => {
247-
handlerResult = values;
248-
}),
249-
);
234+
.command('greet', opt.options({}), ({ values }) => {
235+
handlerResult = values;
236+
});
250237

251238
await cli.parseAsync(['greet', '--name', 'Alice']);
252239

@@ -350,15 +337,15 @@ describe('positionals', () => {
350337
it('parses positional arguments', async () => {
351338
let handlerResult: unknown;
352339

353-
const cli = bargs.create('test-cli').command(
354-
'echo',
355-
pipe(
340+
const cli = bargs
341+
.create('test-cli')
342+
.command(
343+
'echo',
356344
pos.positionals(pos.string({ name: 'message', required: true })),
357-
handle(({ positionals }) => {
345+
({ positionals }) => {
358346
handlerResult = positionals;
359-
}),
360-
),
361-
);
347+
},
348+
);
362349

363350
await cli.parseAsync(['echo', 'Hello, world!']);
364351

@@ -368,18 +355,18 @@ describe('positionals', () => {
368355
it('handles multiple positionals', async () => {
369356
let handlerResult: unknown;
370357

371-
const cli = bargs.create('test-cli').command(
372-
'copy',
373-
pipe(
358+
const cli = bargs
359+
.create('test-cli')
360+
.command(
361+
'copy',
374362
pos.positionals(
375363
pos.string({ name: 'source', required: true }),
376364
pos.string({ name: 'dest', required: true }),
377365
),
378-
handle(({ positionals }) => {
366+
({ positionals }) => {
379367
handlerResult = positionals;
380-
}),
381-
),
382-
);
368+
},
369+
);
383370

384371
await cli.parseAsync(['copy', 'src.txt', 'dst.txt']);
385372

@@ -389,15 +376,15 @@ describe('positionals', () => {
389376
it('handles variadic positionals', async () => {
390377
let handlerResult: unknown;
391378

392-
const cli = bargs.create('test-cli').command(
393-
'concat',
394-
pipe(
379+
const cli = bargs
380+
.create('test-cli')
381+
.command(
382+
'concat',
395383
pos.positionals(pos.variadic('string', { name: 'files' })),
396-
handle(({ positionals }) => {
384+
({ positionals }) => {
397385
handlerResult = positionals;
398-
}),
399-
),
400-
);
386+
},
387+
);
401388

402389
await cli.parseAsync(['concat', 'a.txt', 'b.txt', 'c.txt']);
403390

0 commit comments

Comments
 (0)