cli-forge 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/composable-builder.d.ts +5 -1
- package/dist/lib/composable-builder.js +24 -3
- package/dist/lib/composable-builder.js.map +1 -1
- package/dist/lib/interactive-shell.js +2 -0
- package/dist/lib/interactive-shell.js.map +1 -1
- package/dist/lib/internal-cli.d.ts +18 -5
- package/dist/lib/internal-cli.js +134 -36
- package/dist/lib/internal-cli.js.map +1 -1
- package/dist/lib/public-api.d.ts +11 -2
- package/dist/lib/public-api.js.map +1 -1
- package/package.json +2 -2
- package/src/lib/cli-localization.spec.ts +7 -7
- package/src/lib/composable-builder.spec.ts +73 -0
- package/src/lib/composable-builder.ts +26 -5
- package/src/lib/interactive-shell.ts +2 -0
- package/src/lib/internal-cli.spec.ts +686 -3
- package/src/lib/internal-cli.ts +173 -54
- package/src/lib/public-api.ts +25 -10
- package/tsconfig.lib.json.tsbuildinfo +1 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { makeComposableBuilder } from './composable-builder';
|
|
3
|
+
import cli from './public-api';
|
|
4
|
+
import { chain } from '@cli-forge/parser';
|
|
5
|
+
|
|
6
|
+
describe('makeComposableBuilder', () => {
|
|
7
|
+
describe('capture-and-replay', () => {
|
|
8
|
+
it('should produce stable middleware references across applications', () => {
|
|
9
|
+
const builder = makeComposableBuilder((cmd) =>
|
|
10
|
+
cmd
|
|
11
|
+
.option('verbose', { type: 'boolean' })
|
|
12
|
+
.middleware((args: any) => args)
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const cli1 = builder(cli('test1'));
|
|
16
|
+
const cli2 = builder(cli('test2'));
|
|
17
|
+
|
|
18
|
+
const mw1 = [...(cli1 as any).registeredMiddleware];
|
|
19
|
+
const mw2 = [...(cli2 as any).registeredMiddleware];
|
|
20
|
+
expect(mw1.length).toBe(1);
|
|
21
|
+
expect(mw2.length).toBe(1);
|
|
22
|
+
expect(mw1[0]).toBe(mw2[0]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should correctly replay option registrations', async () => {
|
|
26
|
+
const builder = makeComposableBuilder((cmd) =>
|
|
27
|
+
cmd.option('verbose', { type: 'boolean' })
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
let handlerArgs: any;
|
|
31
|
+
await chain(cli('test'), builder)
|
|
32
|
+
.command('$0', {
|
|
33
|
+
handler: (args) => {
|
|
34
|
+
handlerArgs = args;
|
|
35
|
+
},
|
|
36
|
+
})
|
|
37
|
+
.forge(['--verbose']);
|
|
38
|
+
expect(handlerArgs.verbose).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should deduplicate middleware when builder applied to parent and child', async () => {
|
|
42
|
+
let mwCallCount = 0;
|
|
43
|
+
const builder = makeComposableBuilder((cmd) =>
|
|
44
|
+
cmd.option('verbose', { type: 'boolean' }).middleware((args: any) => {
|
|
45
|
+
mwCallCount++;
|
|
46
|
+
return args;
|
|
47
|
+
})
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
await chain(cli('parent'), builder)
|
|
51
|
+
.command('child', {
|
|
52
|
+
builder: (cmd) =>
|
|
53
|
+
chain(cmd, builder).option('format', { type: 'string' }),
|
|
54
|
+
handler: () => { /* noop */ },
|
|
55
|
+
})
|
|
56
|
+
.forge(['child']);
|
|
57
|
+
expect(mwCallCount).toBe(1);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should replay commands registered by the builder', () => {
|
|
61
|
+
const builder = makeComposableBuilder((cmd) =>
|
|
62
|
+
cmd.command('sub', {
|
|
63
|
+
builder: (c) => c.option('flag', { type: 'boolean' }),
|
|
64
|
+
handler: () => { /* noop */ },
|
|
65
|
+
})
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const myCli = builder(cli('test'));
|
|
69
|
+
const children = myCli.getChildren();
|
|
70
|
+
expect(children).toHaveProperty('sub');
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ParsedArgs } from '@cli-forge/parser';
|
|
1
|
+
import type { ParsedArgs } from '@cli-forge/parser';
|
|
2
2
|
import { CLI } from './public-api';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -30,6 +30,10 @@ export type ComposableBuilder<
|
|
|
30
30
|
* Can be used to add options, commands, or any other CLI modifications.
|
|
31
31
|
* Children added by the builder function are properly tracked in the type.
|
|
32
32
|
*
|
|
33
|
+
* The builder function runs once at creation time against a recording Proxy.
|
|
34
|
+
* Subsequent applications replay the captured operations, ensuring inline
|
|
35
|
+
* middleware closures have stable references for Set-based deduplication.
|
|
36
|
+
*
|
|
33
37
|
* @typeParam TArgs2 - The args type after the builder runs
|
|
34
38
|
* @typeParam TChildren2 - The children type added by the builder
|
|
35
39
|
*/
|
|
@@ -43,15 +47,32 @@ export function makeComposableBuilder<
|
|
|
43
47
|
init: CLI<ParsedArgs, any, {}, any>
|
|
44
48
|
) => CLI<TArgs2, any, TChildren2, any>
|
|
45
49
|
) {
|
|
50
|
+
// Run builder once against a recording proxy to capture operations.
|
|
51
|
+
// Replaying these ensures inline closures (e.g. middleware) keep stable
|
|
52
|
+
// references across applications, enabling Set-based deduplication.
|
|
53
|
+
const operations: { method: string; args: any[] }[] = [];
|
|
54
|
+
const proxy = new Proxy({} as CLI, {
|
|
55
|
+
get(_target, prop) {
|
|
56
|
+
return (...args: any[]) => {
|
|
57
|
+
operations.push({ method: prop as string, args });
|
|
58
|
+
return proxy;
|
|
59
|
+
};
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
fn(proxy);
|
|
63
|
+
|
|
46
64
|
return <TInit extends ParsedArgs, THandlerReturn, TChildren, TParent>(
|
|
47
65
|
init: CLI<TInit, THandlerReturn, TChildren, TParent>
|
|
48
|
-
) =>
|
|
49
|
-
|
|
50
|
-
|
|
66
|
+
) => {
|
|
67
|
+
let current: any = init;
|
|
68
|
+
for (const op of operations) {
|
|
69
|
+
current = current[op.method](...op.args);
|
|
70
|
+
}
|
|
71
|
+
return current as unknown as CLI<
|
|
51
72
|
TInit & TArgs2,
|
|
52
73
|
THandlerReturn,
|
|
53
74
|
TChildren & TChildren2,
|
|
54
75
|
TParent
|
|
55
76
|
>;
|
|
77
|
+
};
|
|
56
78
|
}
|
|
57
|
-
|