Skip to content

Commit d331a48

Browse files
committed
feat(hf): add support dataParser optional, transform and pipe to command params
1 parent 15660cd commit d331a48

14 files changed

Lines changed: 359 additions & 33 deletions

File tree

docs/examples/v0/api/command/create/advanced.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,12 @@ const deployCommand = SC.create(
2727
({ options: { environment }, subject }) => {
2828
type check = ExpectType<
2929
typeof subject,
30-
`v${number}.${number}.${number}` | "latest" | undefined,
30+
`v${number}.${number}.${number}` | "latest",
31+
"strict"
32+
>;
33+
type checkOption = ExpectType<
34+
typeof environment,
35+
"staging" | "prod",
3136
"strict"
3237
>;
3338
},

docs/examples/v0/api/command/exec/main.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import { DP } from "@duplojs/utils";
33

44
await SC.exec(
55
{
6+
options: [SC.createBooleanOption("version")],
67
subject: DP.tuple([DP.string(), DP.string()]),
78
},
8-
({ subject }) => {
9-
if (!subject) {
9+
({ subject, options }) => {
10+
if (options.version) {
11+
console.log("1.0.0");
1012
return;
1113
}
1214

@@ -18,3 +20,6 @@ await SC.exec(
1820
// Try with:
1921
// cpCli file.txt file-copy.txt
2022
// output: copy file.txt to file-copy.txt
23+
// Other:
24+
// cpCli --version
25+
// output: 1.0.0

docs/libs/v0/command/create.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function create(...args) {
5656
});
5757
}
5858
else if (utils.DP.identifier(self.subject, utils.DP.dataParserKind)) {
59-
if (commandOptions.restArgs.length !== 1) {
59+
if (commandOptions.restArgs.length > 1) {
6060
throw new errors.CommandManyArgumentsError(commandOptions.restArgs.length);
6161
}
6262
await execute({

docs/libs/v0/command/create.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export interface CreateCommandExecuteParams<GenericOptions extends readonly Opti
2929
name: GenericOptionName;
3030
}>["execute"]>["result"];
3131
};
32-
subject?: DP.Output<GenericSubject>;
32+
subject: DP.Output<GenericSubject>;
3333
}
3434
/**
3535
* Create a command node.

docs/libs/v0/command/create.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function create(...args) {
5454
});
5555
}
5656
else if (DP.identifier(self.subject, DP.dataParserKind)) {
57-
if (commandOptions.restArgs.length !== 1) {
57+
if (commandOptions.restArgs.length > 1) {
5858
throw new CommandManyArgumentsError(commandOptions.restArgs.length);
5959
}
6060
await execute({

docs/libs/v0/command/types/eligibleDataParser.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,11 @@ export type EligibleDataParser = (DP.DataParserString | DP.DataParserNumber<Simp
1616
EligibleDataParser,
1717
...EligibleDataParser[]
1818
];
19+
}>> | DP.DataParserTransform<SimplifyTopLevel<Omit<DP.DataParserDefinitionTransform, "inner"> & {
20+
readonly inner: EligibleDataParser;
21+
}>> | DP.DataParserPipe<SimplifyTopLevel<Omit<DP.DataParserDefinitionPipe, "input" | "output"> & {
22+
readonly input: EligibleDataParser;
23+
readonly output: EligibleDataParser;
24+
}>> | DP.DataParserOptional<SimplifyTopLevel<Omit<DP.DataParserDefinitionOptional, "inner"> & {
25+
readonly inner: EligibleDataParser;
1926
}>>);

integration/node/index.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ describe("node integration", () => {
254254
>;
255255
type _CheckSubject = ExpectType<
256256
typeof subject,
257-
[string] | undefined,
257+
[string],
258258
"strict"
259259
>;
260260

scripts/command/create.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export interface CreateCommandExecuteParams<
6767
>["execute"]
6868
>["result"]
6969
};
70-
subject?: DP.Output<GenericSubject>;
70+
subject: DP.Output<GenericSubject>;
7171
}
7272

7373
/**
@@ -160,7 +160,7 @@ export function create(
160160
subject: self.subject.parseOrThrow(commandOptions.restArgs),
161161
});
162162
} else if (DP.identifier(self.subject, DP.dataParserKind)) {
163-
if (commandOptions.restArgs.length !== 1) {
163+
if (commandOptions.restArgs.length > 1) {
164164
throw new CommandManyArgumentsError(commandOptions.restArgs.length);
165165
}
166166

scripts/command/types/eligibleDataParser.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,29 @@ export type EligibleDataParser = (
6262
}
6363
>
6464
>
65+
| DP.DataParserTransform<
66+
SimplifyTopLevel<
67+
& Omit<DP.DataParserDefinitionTransform, "inner">
68+
& {
69+
readonly inner: EligibleDataParser;
70+
}
71+
>
72+
>
73+
| DP.DataParserPipe<
74+
SimplifyTopLevel<
75+
& Omit<DP.DataParserDefinitionPipe, "input" | "output">
76+
& {
77+
readonly input: EligibleDataParser;
78+
readonly output: EligibleDataParser;
79+
}
80+
>
81+
>
82+
| DP.DataParserOptional<
83+
SimplifyTopLevel<
84+
& Omit<DP.DataParserDefinitionOptional, "inner">
85+
& {
86+
readonly inner: EligibleDataParser;
87+
}
88+
>
89+
>
6590
);

tests/command/create.test.ts

Lines changed: 132 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type ExpectType, DP } from "@duplojs/utils";
1+
import { type ExpectType, DPE, DP, S } from "@duplojs/utils";
22
import { DServerCommand, TESTImplementation, setEnvironment } from "@scripts";
33

44
describe("create", () => {
@@ -20,6 +20,16 @@ describe("create", () => {
2020
string,
2121
"strict"
2222
>;
23+
type _CheckDescription = ExpectType<
24+
typeof command.description,
25+
string | null,
26+
"strict"
27+
>;
28+
type _CheckSubject = ExpectType<
29+
typeof command.subject,
30+
DServerCommand.Subject | null | readonly DServerCommand.Command[],
31+
"strict"
32+
>;
2333

2434
expect(command.name).toBe("root");
2535
expect(command.description).toBeNull();
@@ -55,11 +65,27 @@ describe("create", () => {
5565
{
5666
options: [
5767
DServerCommand.createBooleanOption("verbose", { aliases: ["v"] }),
58-
DServerCommand.createOption("name", DP.string(), { required: true }),
68+
DServerCommand.createOption("name", DPE.string(), { required: true }),
5969
],
6070
subject: DP.tuple([DP.string()]),
6171
},
62-
executeSpy,
72+
(params) => {
73+
type _CheckOptions = ExpectType<
74+
typeof params.options,
75+
{
76+
verbose: boolean;
77+
name: string;
78+
},
79+
"strict"
80+
>;
81+
type _CheckSubject = ExpectType<
82+
typeof params.subject,
83+
[string],
84+
"strict"
85+
>;
86+
87+
return executeSpy(params);
88+
},
6389
);
6490

6591
await command.execute(["--verbose", "--name=duplo", "subject"]);
@@ -81,9 +107,17 @@ describe("create", () => {
81107
const command = DServerCommand.create(
82108
"root",
83109
{
84-
subject: DP.string(),
110+
subject: DPE.string().optional(),
111+
},
112+
({ subject }) => {
113+
type check = ExpectType<
114+
typeof subject,
115+
string | undefined,
116+
"strict"
117+
>;
118+
119+
return undefined;
85120
},
86-
() => undefined,
87121
);
88122

89123
await expect(command.execute(["one", "two"])).rejects.toThrowError(DServerCommand.CommandManyArgumentsError);
@@ -126,9 +160,20 @@ describe("create", () => {
126160
}),
127161
} as never,
128162
],
129-
subject: DP.string(),
163+
subject: DPE.string(),
164+
},
165+
({ options, subject }) => {
166+
type _CheckSubject = ExpectType<
167+
typeof subject,
168+
string,
169+
"strict"
170+
>;
171+
172+
executeSpy({
173+
options,
174+
subject,
175+
});
130176
},
131-
executeSpy,
132177
);
133178

134179
await command.execute(["subject"]);
@@ -142,6 +187,86 @@ describe("create", () => {
142187
expect(exitSpy).toHaveBeenCalledWith(0);
143188
});
144189

190+
it("infers typed params with array subject", async() => {
191+
setEnvironment("TEST");
192+
const exitSpy = vi.fn();
193+
const executeSpy = vi.fn();
194+
TESTImplementation.set("exitProcess", exitSpy);
195+
196+
const command = DServerCommand.create(
197+
"root",
198+
{
199+
subject: DPE.string().array(),
200+
},
201+
(params) => {
202+
type _CheckSubject = ExpectType<
203+
typeof params.subject,
204+
string[],
205+
"strict"
206+
>;
207+
208+
executeSpy(params);
209+
},
210+
);
211+
212+
await command.execute(["one", "two"]);
213+
214+
expect(executeSpy).toHaveBeenCalledWith({
215+
options: {},
216+
subject: ["one", "two"],
217+
});
218+
expect(exitSpy).toHaveBeenCalledWith(0);
219+
});
220+
221+
it("executes optional option with pipe and transform parser without runtime bug", async() => {
222+
setEnvironment("TEST");
223+
const exitSpy = vi.fn();
224+
const executeSpy = vi.fn();
225+
TESTImplementation.set("exitProcess", exitSpy);
226+
227+
const command = DServerCommand.create(
228+
"root",
229+
{
230+
options: [
231+
DServerCommand.createOption(
232+
"name",
233+
DPE.string().pipe(
234+
DPE.transform(
235+
DPE.string(),
236+
S.toUpperCase,
237+
),
238+
),
239+
),
240+
],
241+
},
242+
({ options }) => {
243+
type _CheckOptions = ExpectType<
244+
typeof options.name,
245+
Uppercase<string> | undefined,
246+
"strict"
247+
>;
248+
249+
executeSpy({ options });
250+
},
251+
);
252+
253+
await command.execute([]);
254+
await command.execute(["--name=guest"]);
255+
256+
expect(executeSpy).toHaveBeenNthCalledWith(1, {
257+
options: {
258+
name: undefined,
259+
},
260+
});
261+
expect(executeSpy).toHaveBeenNthCalledWith(2, {
262+
options: {
263+
name: "GUEST",
264+
},
265+
});
266+
expect(exitSpy).toHaveBeenNthCalledWith(1, 0);
267+
expect(exitSpy).toHaveBeenNthCalledWith(2, 0);
268+
});
269+
145270
it("executes matching child command when subject is a command list", async() => {
146271
setEnvironment("TEST");
147272
const exitSpy = vi.fn();

0 commit comments

Comments
 (0)