Skip to content
This repository was archived by the owner on Dec 12, 2025. It is now read-only.

Commit 8dc7dd7

Browse files
committed
docs: add lazy loading and use Cli instead
1 parent 3e3f028 commit 8dc7dd7

File tree

11 files changed

+341
-67
lines changed

11 files changed

+341
-67
lines changed

docs/.vitepress/plugins/markdownTransform.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const TAB_SIZE = 2;
55
// @keep-sorted
66
export const clercImports = [
77
"Clerc",
8+
"Cli",
89
"completionsPlugin",
910
"friendlyErrorPlugin",
1011
"helpPlugin",

docs/advanced.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ title: Advanced Usage
99
Clerc allows you to pass a custom array of arguments instead of using the default `process.argv` / `Deno.args`. This is useful for testing or specific environments.
1010

1111
```ts
12-
Clerc.create().parse(["node", "my-cli", "greet"]); // Pass a custom array of arguments
12+
Cli().parse(["node", "my-cli", "greet"]); // Pass a custom array of arguments
1313
```
1414

1515
Alternatively, you can also pass an argument object:
1616

1717
```ts
18-
Clerc.create().parse({
18+
Cli().parse({
1919
argv: ["greet"],
2020
});
2121
```
@@ -25,12 +25,13 @@ Clerc.create().parse({
2525
Sometimes you may want to parse commands and flags without immediately executing the command handler. Clerc provides an option to achieve this:
2626

2727
```ts
28-
const result = Clerc.create().parse({
28+
const result = Cli().parse({
2929
run: false, // Parse only, do not execute
3030
});
3131
```
3232

3333
When you need to run, you can call:
3434

3535
```ts
36-
result.run(); // Execute the parsed command
36+
result.run(); // Execute the parsed command
37+
```

docs/commands.md

Lines changed: 146 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ title: Commands
77
## Basic Usage
88

99
```ts
10-
import { Clerc } from "clerc";
11-
12-
const cli = Clerc.create()
10+
const cli = Cli()
1311
.scriptName("foo-cli")
1412
.description("A simple CLI")
1513
.version("1.0.0")
@@ -33,7 +31,7 @@ Command aliases allow users to invoke a command using an alternative name. This
3331
You can define a single alias for a command using a string:
3432

3533
```ts
36-
const cli = Clerc.create()
34+
const cli = Cli()
3735
.scriptName("foo-cli")
3836
.description("A simple CLI")
3937
.version("1.0.0")
@@ -53,7 +51,7 @@ Now both `foo-cli foo` and `foo-cli f` will output "It works!".
5351
You can define multiple aliases for a command using an array:
5452

5553
```ts
56-
const cli = Clerc.create()
54+
const cli = Cli()
5755
.scriptName("foo-cli")
5856
.description("A simple CLI")
5957
.version("1.0.0")
@@ -73,7 +71,7 @@ Now `foo-cli foo`, `foo-cli f`, `foo-cli bar`, and `foo-cli baz` all work the sa
7371
#### Example: Git-like Abbreviations
7472

7573
```ts
76-
const cli = Clerc.create()
74+
const cli = Cli()
7775
.scriptName("git")
7876
.command("status", "Show working tree status", {
7977
alias: "st",
@@ -112,7 +110,7 @@ $ git co
112110
You can define subcommands by using spaces in the command name:
113111

114112
```ts
115-
const cli = Clerc.create()
113+
const cli = Cli()
116114
.scriptName("foo-cli")
117115
.description("A simple CLI")
118116
.version("1.0.0")
@@ -128,7 +126,7 @@ const cli = Clerc.create()
128126
You can define a root command (a command with no name) to handle cases when no subcommand is specified:
129127

130128
```ts
131-
const cli = Clerc.create()
129+
const cli = Cli()
132130
.scriptName("foo-cli")
133131
.description("A simple CLI")
134132
.version("1.0.0")
@@ -162,7 +160,7 @@ Example:
162160
```ts
163161
// $ node ./foo-cli.mjs a b c d
164162

165-
const cli = Clerc.create()
163+
const cli = Cli()
166164
.scriptName("foo-cli")
167165
.description("A simple CLI")
168166
.version("1.0.0")
@@ -202,7 +200,7 @@ Example:
202200
```ts
203201
// $ node ./foo-cli.mjs echo -- hello world
204202

205-
const cli = Clerc.create()
203+
const cli = Cli()
206204
.scriptName("foo-cli")
207205
.description("A simple CLI")
208206
.version("1.0.0")
@@ -235,11 +233,11 @@ Where `--flag` is passed directly to the script, not to `deno`.
235233
You can achieve this usage by using the `ignore` property to specify which arguments or flags to ignore.
236234

237235
```ts
238-
import { Clerc, PARAMETER } from "clerc";
236+
import { PARAMETER } from "clerc";
239237

240238
let encounteredParameter = false;
241239

242-
const cli = Clerc.create()
240+
const cli = Cli()
243241
.scriptName("deno")
244242
.description("Deno CLI")
245243
.version("1.0.0")
@@ -275,7 +273,7 @@ const cli = Clerc.create()
275273
To separate the handler from the cli definition, you can use the `defineCommand` utility function:
276274

277275
```ts
278-
import { Clerc, defineCommand } from "clerc";
276+
import { defineCommand } from "clerc";
279277

280278
const command = defineCommand({
281279
name: "test",
@@ -287,10 +285,144 @@ const command = defineCommand({
287285
},
288286
});
289287

290-
const cli = Clerc.create()
288+
const cli = Cli()
291289
.scriptName("foo-cli")
292290
.description("A simple CLI")
293291
.version("1.0.0")
294292
.command(command)
295293
.parse();
296294
```
295+
296+
## Lazy Loading
297+
298+
Lazy loading allows you to defer the loading of command handlers until they are actually invoked. This is useful for reducing startup time and memory usage, especially when you have many commands or heavy handlers.
299+
300+
You can implement lazy loading by using dynamic imports (`await import()`) within the handler:
301+
302+
### Basic Lazy Loading
303+
304+
```ts
305+
const cli = Cli()
306+
.scriptName("app")
307+
.description("An application with lazy loading")
308+
.version("1.0.0")
309+
.command("build", "Build the project", {
310+
flags: {
311+
production: {
312+
type: Boolean,
313+
description: "Build for production",
314+
},
315+
},
316+
})
317+
.on("build", async (ctx) => {
318+
// Handler is only loaded when the command is invoked
319+
const { buildProject } = await import("./handlers/build.js");
320+
await buildProject(ctx);
321+
})
322+
.command("deploy", "Deploy the application", {
323+
flags: {
324+
environment: {
325+
type: String,
326+
default: "staging",
327+
description: "Target environment",
328+
},
329+
},
330+
})
331+
.on("deploy", async (ctx) => {
332+
// Another handler loaded lazily
333+
const { deploy } = await import("./handlers/deploy.js");
334+
await deploy(ctx);
335+
})
336+
.parse();
337+
```
338+
339+
### Lazy Loading with defineCommand
340+
341+
You can also combine lazy loading with the `defineCommand` utility:
342+
343+
```ts
344+
import { defineCommand } from "clerc";
345+
346+
const command = defineCommand({
347+
name: "migrate",
348+
description: "Run database migrations",
349+
flags: {},
350+
parameters: [],
351+
handler: async (ctx) => {
352+
// Handler loaded only when command is invoked
353+
const { runMigrations } = await import("./handlers/migrate.js");
354+
await runMigrations(ctx);
355+
},
356+
});
357+
358+
const cli = Cli()
359+
.scriptName("app")
360+
.description("Application with lazy-loaded commands")
361+
.version("1.0.0")
362+
.command(command)
363+
.parse();
364+
```
365+
366+
### Benefits
367+
368+
- **Faster startup time**: Only handlers for invoked commands are loaded
369+
- **Lower memory usage**: Unused handlers don't consume memory
370+
- **Better scalability**: Easy to add many commands without performance impact
371+
- **Asynchronous operations**: Handlers can perform async operations like file I/O or network requests
372+
373+
### Example: Modular Command Structure
374+
375+
Directory structure:
376+
377+
```
378+
project/
379+
├── cli.ts
380+
├── handlers/
381+
│ ├── build.ts
382+
│ ├── dev.ts
383+
│ ├── deploy.ts
384+
│ └── test.ts
385+
```
386+
387+
`handlers/build.ts`:
388+
389+
```ts
390+
export async function buildProject(ctx) {
391+
if (ctx.flags.production) {
392+
console.log("Building for production...");
393+
} else {
394+
console.log("Building for development...");
395+
}
396+
}
397+
```
398+
399+
`cli.ts`:
400+
401+
```ts
402+
const cli = Cli()
403+
.scriptName("app")
404+
.version("1.0.0")
405+
.command("build", "Build the project", {
406+
flags: {
407+
production: {
408+
type: Boolean,
409+
description: "Build for production",
410+
},
411+
},
412+
})
413+
.on("build", async (ctx) => {
414+
const { buildProject } = await import("./handlers/build.js");
415+
await buildProject(ctx);
416+
})
417+
.command("dev", "Start development server", {})
418+
.on("dev", async (ctx) => {
419+
const { startDev } = await import("./handlers/dev.js");
420+
await startDev(ctx);
421+
})
422+
.command("deploy", "Deploy application")
423+
.on("deploy", async (ctx) => {
424+
const { deploy } = await import("./handlers/deploy.js");
425+
await deploy(ctx);
426+
})
427+
.parse();
428+
```

docs/flags.md

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Flag aliases allow users to use shorter or alternative names for flags. This is
3232
You can define a single alias for a flag using a string:
3333

3434
```ts
35-
const cli = Clerc.create()
35+
const cli = Cli()
3636
.command("build", "Build the project", {
3737
flags: {
3838
output: {
@@ -64,7 +64,7 @@ const cli = Clerc.create()
6464
You can define multiple aliases for a flag using an array:
6565

6666
```ts
67-
const cli = Clerc.create()
67+
const cli = Cli()
6868
.command("config", "Configure the application", {
6969
flags: {
7070
config: {
@@ -97,7 +97,7 @@ const cli = Clerc.create()
9797
When using short aliases (single characters), they can be combined together:
9898

9999
```ts
100-
const cli = Clerc.create()
100+
const cli = Cli()
101101
.command("compress", "Compress files", {
102102
flags: {
103103
output: {
@@ -133,7 +133,7 @@ const cli = Clerc.create()
133133
```ts
134134
// $ node ./foo-cli.mjs echo --some-boolean --some-string hello --some-number 1 -n 2
135135

136-
const cli = Clerc.create()
136+
const cli = Cli()
137137
.scriptName("foo-cli")
138138
.description("A simple CLI")
139139
.version("1.0.0")
@@ -189,7 +189,7 @@ The `String` type is used for flags that accept string values. This is the most
189189
**Default value behavior:** If the flag is not specified, its value is `undefined` (unless a `default` property is set).
190190

191191
```ts
192-
const cli = Clerc.create()
192+
const cli = Cli()
193193
.command("greet", "Greet someone", {
194194
flags: {
195195
name: {
@@ -223,7 +223,7 @@ The `Boolean` type is used for creating boolean switch flags. By default, simply
223223
**Default value behavior:** If the flag is not specified, its value is `false`.
224224

225225
```ts
226-
const cli = Clerc.create()
226+
const cli = Cli()
227227
.command("build", "Build the project", {
228228
flags: {
229229
production: {
@@ -255,7 +255,7 @@ const cli = Clerc.create()
255255
The Boolean type supports a `negatable` property that allows you to decide whether to enable negated flags. By default, `negatable` is `true`, which means `--no-flag` will set the `flag` flag to `false`.
256256

257257
```ts
258-
const cli = Clerc.create()
258+
const cli = Cli()
259259
.command("start", "Start the application", {
260260
flags: {
261261
color: {
@@ -296,7 +296,7 @@ The `Array` type is used for flags that accept multiple values. Define it by wra
296296
**Default value behavior:** If the flag is not specified, its value is `[]` (empty array).
297297

298298
```ts
299-
const cli = Clerc.create()
299+
const cli = Cli()
300300
.command("copy", "Copy files", {
301301
flags: {
302302
// Use [String] to accept multiple string values
@@ -345,7 +345,7 @@ The counter type is used to count how many times a flag is specified. This can b
345345
**Default value behavior:** If the flag is not specified, its value is `0`.
346346

347347
```ts
348-
const cli = Clerc.create()
348+
const cli = Cli()
349349
.command("log", "Display logs", {
350350
flags: {
351351
// [Boolean] type counts how many times the flag is used
@@ -379,7 +379,7 @@ The `Object` type is used for flags that accept key-value pairs. Use dots or oth
379379
**Default value behavior:** If the flag is not specified, its value is `{}` (empty object).
380380

381381
```ts
382-
const cli = Clerc.create()
382+
const cli = Cli()
383383
.command("config", "Configure the application", {
384384
flags: {
385385
define: {
@@ -408,7 +408,7 @@ Clerc provides some built-in advanced flag types to facilitate common needs:
408408
```ts
409409
import { Choices } from "clerc";
410410

411-
Clerc.create()
411+
Cli()
412412
.command("serve", "Start the server", {
413413
flags: {
414414
mode: {
@@ -434,7 +434,7 @@ You can create custom flag types by providing a custom type function. The type f
434434
const CommaSeparatedList = (value: string): string[] =>
435435
value.split(",").map((item) => item.trim());
436436

437-
const cli = Clerc.create()
437+
const cli = Cli()
438438
.scriptName("custom-cli")
439439
.description("A CLI using a custom flag type")
440440
.version("1.0.0")

0 commit comments

Comments
 (0)