@moostjs/event-cli 0.5.33 → 0.6.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/README.md +25 -3
- package/dist/index.cjs +32 -9
- package/dist/index.d.ts +12 -12
- package/dist/index.mjs +15 -10
- package/package.json +38 -32
- package/scripts/setup-skills.js +78 -0
- package/skills/moostjs-event-cli/SKILL.md +38 -0
- package/skills/moostjs-event-cli/advanced.md +202 -0
- package/skills/moostjs-event-cli/commands.md +152 -0
- package/skills/moostjs-event-cli/controllers.md +142 -0
- package/skills/moostjs-event-cli/core.md +114 -0
- package/skills/moostjs-event-cli/help.md +160 -0
- package/skills/moostjs-event-cli/interceptors.md +160 -0
- package/skills/moostjs-event-cli/options.md +180 -0
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Advanced — @moostjs/event-cli
|
|
2
|
+
|
|
3
|
+
> Manual adapter setup, Wooks composables, DI scopes, multi-adapter patterns, and cliKind.
|
|
4
|
+
|
|
5
|
+
## Concepts
|
|
6
|
+
|
|
7
|
+
`CliApp` handles the common case. For advanced scenarios — sharing a Moost instance across adapters, using Wooks composables directly, or fine-tuning DI — use `MoostCli` as a standalone adapter.
|
|
8
|
+
|
|
9
|
+
Wooks composables are functions that access the current CLI event context (via AsyncLocalStorage). They work anywhere in the handler call chain — in handlers, services, or interceptors.
|
|
10
|
+
|
|
11
|
+
## API Reference
|
|
12
|
+
|
|
13
|
+
### `MoostCli`
|
|
14
|
+
|
|
15
|
+
Low-level CLI adapter class implementing `TMoostAdapter<TCliHandlerMeta>`.
|
|
16
|
+
|
|
17
|
+
Constructor options (`TMoostCliOpts`):
|
|
18
|
+
- `wooksCli?: WooksCli | TWooksCliOptions` — a WooksCli instance or configuration options
|
|
19
|
+
- `debug?: boolean` — enable internal logging (default: `false`)
|
|
20
|
+
- `globalCliOptions?: { keys: string[]; description?: string; type?: TFunction }[]` — options shown in help for every command
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { MoostCli, cliHelpInterceptor } from '@moostjs/event-cli'
|
|
24
|
+
import { Moost } from 'moost'
|
|
25
|
+
|
|
26
|
+
const app = new Moost()
|
|
27
|
+
|
|
28
|
+
app.applyGlobalInterceptors(
|
|
29
|
+
cliHelpInterceptor({ colors: true, lookupLevel: 3 }),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
app.registerControllers(AppController)
|
|
33
|
+
|
|
34
|
+
app.adapter(new MoostCli({
|
|
35
|
+
wooksCli: {
|
|
36
|
+
cliHelp: { name: 'my-cli' },
|
|
37
|
+
},
|
|
38
|
+
globalCliOptions: [
|
|
39
|
+
{ keys: ['help'], description: 'Display instructions for the command.' },
|
|
40
|
+
],
|
|
41
|
+
}))
|
|
42
|
+
|
|
43
|
+
void app.init()
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### When to use MoostCli vs CliApp
|
|
47
|
+
|
|
48
|
+
| Use case | Recommendation |
|
|
49
|
+
|----------|---------------|
|
|
50
|
+
| Standard CLI app | `CliApp` — less boilerplate |
|
|
51
|
+
| Pre-configured WooksCli instance | `MoostCli` — pass your own instance |
|
|
52
|
+
| Multiple adapters (CLI + HTTP) | `MoostCli` — attach to existing Moost instance |
|
|
53
|
+
| Custom error handling on WooksCli | `MoostCli` — configure `wooksCli.onError` |
|
|
54
|
+
|
|
55
|
+
### `cliKind`
|
|
56
|
+
|
|
57
|
+
Event type identifier for CLI events, re-exported from `@wooksjs/event-cli`. Useful when writing adapters or composables that distinguish event types.
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { cliKind } from '@moostjs/event-cli'
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Wooks composables
|
|
64
|
+
|
|
65
|
+
These are from `@wooksjs/event-cli` and `@wooksjs/event-core`. They work inside any handler or service during a CLI event.
|
|
66
|
+
|
|
67
|
+
### `useCliOption(name: string)`
|
|
68
|
+
|
|
69
|
+
Read a single option value. This is what `@CliOption()` uses under the hood.
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import { useCliOption } from '@wooksjs/event-cli'
|
|
73
|
+
|
|
74
|
+
@Cli('build')
|
|
75
|
+
build() {
|
|
76
|
+
const verbose = useCliOption('verbose')
|
|
77
|
+
if (verbose) console.log('Verbose mode on')
|
|
78
|
+
return 'Building...'
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### `useCliOptions()`
|
|
83
|
+
|
|
84
|
+
Read all parsed CLI options at once.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { useCliOptions } from '@wooksjs/event-cli'
|
|
88
|
+
|
|
89
|
+
@Cli('build')
|
|
90
|
+
build() {
|
|
91
|
+
const opts = useCliOptions()
|
|
92
|
+
// opts: { verbose: true, target: 'production', ... }
|
|
93
|
+
return `Building with ${JSON.stringify(opts)}`
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `useRouteParams()`
|
|
98
|
+
|
|
99
|
+
Read positional arguments. This is what `@Param()` uses under the hood.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { useRouteParams } from '@wooksjs/event-core'
|
|
103
|
+
|
|
104
|
+
@Cli('deploy/:env')
|
|
105
|
+
deploy() {
|
|
106
|
+
const params = useRouteParams()
|
|
107
|
+
return `Deploying to ${params.get('env')}`
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `useCliHelp()`
|
|
112
|
+
|
|
113
|
+
Access the help renderer programmatically.
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
import { useCliHelp } from '@wooksjs/event-cli'
|
|
117
|
+
|
|
118
|
+
@Cli('info')
|
|
119
|
+
info() {
|
|
120
|
+
const { print } = useCliHelp()
|
|
121
|
+
print(true) // print help with colors
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Common Patterns
|
|
126
|
+
|
|
127
|
+
### Pattern: Multi-adapter CLI + HTTP
|
|
128
|
+
|
|
129
|
+
A single Moost instance serving both CLI commands and HTTP routes:
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { Cli, Controller, Param } from '@moostjs/event-cli'
|
|
133
|
+
import { Get } from '@moostjs/event-http'
|
|
134
|
+
|
|
135
|
+
@Controller('health')
|
|
136
|
+
export class HealthController {
|
|
137
|
+
@Cli('check')
|
|
138
|
+
@Get('check')
|
|
139
|
+
check() {
|
|
140
|
+
return { status: 'ok', uptime: process.uptime() }
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Wire up both adapters:
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
import { MoostCli } from '@moostjs/event-cli'
|
|
149
|
+
import { MoostHttp } from '@moostjs/event-http'
|
|
150
|
+
import { Moost } from 'moost'
|
|
151
|
+
|
|
152
|
+
const app = new Moost()
|
|
153
|
+
app.registerControllers(HealthController)
|
|
154
|
+
|
|
155
|
+
app.adapter(new MoostCli())
|
|
156
|
+
app.adapter(new MoostHttp()).listen(3000)
|
|
157
|
+
|
|
158
|
+
void app.init()
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Now `my-cli health check` and `GET /health/check` both invoke the same handler.
|
|
162
|
+
|
|
163
|
+
### Pattern: DI scopes in CLI
|
|
164
|
+
|
|
165
|
+
```ts
|
|
166
|
+
import { Injectable } from 'moost'
|
|
167
|
+
|
|
168
|
+
@Injectable('SINGLETON')
|
|
169
|
+
export class ConfigService {
|
|
170
|
+
readonly env = process.env.NODE_ENV ?? 'development'
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@Injectable('FOR_EVENT')
|
|
174
|
+
export class CommandState {
|
|
175
|
+
startedAt = Date.now()
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
- `SINGLETON` — one instance for the app lifetime (shared config, DB connections)
|
|
180
|
+
- `FOR_EVENT` — new instance per CLI command execution (command-specific state)
|
|
181
|
+
|
|
182
|
+
Controllers default to `SINGLETON` scope, which is correct for most CLI apps.
|
|
183
|
+
|
|
184
|
+
## Integration
|
|
185
|
+
|
|
186
|
+
- Wooks composables (`useCliOption`, `useCliOptions`, etc.) are imported from `@wooksjs/event-cli`
|
|
187
|
+
- Route param composables (`useRouteParams`) come from `@wooksjs/event-core`
|
|
188
|
+
- DI decorators (`@Injectable`, `@Inject`, `@Provide`) come from `moost`
|
|
189
|
+
- HTTP decorators (`@Get`, `@Post`, etc.) come from `@moostjs/event-http`
|
|
190
|
+
|
|
191
|
+
## Best Practices
|
|
192
|
+
|
|
193
|
+
- Use `MoostCli` only when `CliApp` isn't flexible enough
|
|
194
|
+
- When `debug` is `false` (default), DI container logging is suppressed for clean terminal output
|
|
195
|
+
- In multi-adapter setups, decorate shared methods with both `@Cli()` and `@Get()`/`@Post()`
|
|
196
|
+
- Composables work in interceptors and services too — not just handlers
|
|
197
|
+
|
|
198
|
+
## Gotchas
|
|
199
|
+
|
|
200
|
+
- `MoostCli` calls `app.init()` — don't call `init()` yourself after attaching the adapter in `CliApp.start()`
|
|
201
|
+
- Wooks composables only work inside an active event context (during handler execution)
|
|
202
|
+
- When using a custom `WooksCli` instance with `MoostCli`, the `onNotFound` callback is overridden by the adapter
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# Commands — @moostjs/event-cli
|
|
2
|
+
|
|
3
|
+
> Defining CLI commands, command paths, positional arguments, aliases, and examples.
|
|
4
|
+
|
|
5
|
+
## Concepts
|
|
6
|
+
|
|
7
|
+
Every CLI command is a method decorated with `@Cli()`. The decorator argument defines the command path — the words a user types to invoke it. Command paths support positional arguments (`:param` segments), space or slash separation, and escaped colons.
|
|
8
|
+
|
|
9
|
+
## API Reference
|
|
10
|
+
|
|
11
|
+
### `@Cli(path?: string)`
|
|
12
|
+
|
|
13
|
+
Method decorator. Registers the method as a CLI command handler.
|
|
14
|
+
|
|
15
|
+
- `path` — command path with segments separated by spaces or `/`. Segments starting with `:` are positional arguments. If omitted, the method name is used as the command.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Cli, Controller } from '@moostjs/event-cli'
|
|
19
|
+
|
|
20
|
+
@Controller()
|
|
21
|
+
export class AppController {
|
|
22
|
+
@Cli('deploy/:env')
|
|
23
|
+
deploy(@Param('env') env: string) {
|
|
24
|
+
return `Deploying to ${env}...`
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### `@CliAlias(alias: string)`
|
|
30
|
+
|
|
31
|
+
Method decorator. Defines an alternative name for a command. Stack multiple decorators for multiple aliases.
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { Cli, CliAlias, Controller, Param } from '@moostjs/event-cli'
|
|
35
|
+
|
|
36
|
+
@Controller()
|
|
37
|
+
export class AppController {
|
|
38
|
+
@Cli('install/:package')
|
|
39
|
+
@CliAlias('i/:package')
|
|
40
|
+
@CliAlias('add/:package')
|
|
41
|
+
install(@Param('package') pkg: string) {
|
|
42
|
+
return `Installing ${pkg}...`
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
All three invoke the same handler:
|
|
48
|
+
```bash
|
|
49
|
+
my-cli install lodash
|
|
50
|
+
my-cli i lodash
|
|
51
|
+
my-cli add lodash
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `@CliExample(cmd: string, description?: string)`
|
|
55
|
+
|
|
56
|
+
Method decorator. Adds a usage example displayed in `--help` output. Stack multiple decorators for multiple examples.
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
import { Cli, CliExample, Controller } from '@moostjs/event-cli'
|
|
60
|
+
|
|
61
|
+
@Controller()
|
|
62
|
+
export class DeployController {
|
|
63
|
+
@Cli('deploy/:env')
|
|
64
|
+
@CliExample('deploy dev -p my-app', 'Deploy to development')
|
|
65
|
+
@CliExample('deploy prod --verbose', 'Deploy with verbose logging')
|
|
66
|
+
deploy(@Param('env') env: string) {
|
|
67
|
+
return `Deploying to ${env}`
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### `@Param(name: string)`
|
|
73
|
+
|
|
74
|
+
Parameter decorator (re-exported from `moost`). Extracts a positional argument from a `:name` segment in the command path.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
@Cli('copy/:source/:dest')
|
|
78
|
+
copy(
|
|
79
|
+
@Param('source') source: string,
|
|
80
|
+
@Param('dest') dest: string,
|
|
81
|
+
) {
|
|
82
|
+
return `Copying ${source} to ${dest}`
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
my-cli copy fileA.txt fileB.txt
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Common Patterns
|
|
91
|
+
|
|
92
|
+
### Pattern: Space-separated vs slash-separated paths
|
|
93
|
+
|
|
94
|
+
Both notations are equivalent. The user always types space-separated words:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
// These are identical:
|
|
98
|
+
@Cli('config set')
|
|
99
|
+
@Cli('config/set')
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
my-cli config set
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Pattern: Default path from method name
|
|
107
|
+
|
|
108
|
+
When `@Cli()` is called without arguments, the method name becomes the command:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
@Cli()
|
|
112
|
+
status() {
|
|
113
|
+
return 'All systems operational'
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
my-cli status
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Pattern: Escaped colons
|
|
122
|
+
|
|
123
|
+
If a command contains a literal colon (like `build:dev`), escape it:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
@Cli('build\\:dev')
|
|
127
|
+
buildDev() {
|
|
128
|
+
return 'Building for dev...'
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
my-cli build:dev
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Pattern: Return values
|
|
137
|
+
|
|
138
|
+
Whatever a handler returns is printed to stdout. Return a string for plain text, or an object/array for JSON:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
@Cli('info')
|
|
142
|
+
info() {
|
|
143
|
+
return { name: 'my-app', version: '1.0.0' }
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Gotchas
|
|
148
|
+
|
|
149
|
+
- Command paths with `:` that are NOT parameters must be escaped with `\\:` (e.g. `'build\\:dev'`)
|
|
150
|
+
- Space-separated and slash-separated paths are equivalent internally — `'config set'` becomes `'config/set'`
|
|
151
|
+
- If you omit the path in `@Cli()`, the method name is used as the command name
|
|
152
|
+
- Multiple `@Cli()` decorators on the same method register multiple commands pointing to the same handler
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# Controllers — @moostjs/event-cli
|
|
2
|
+
|
|
3
|
+
> Organizing commands into logical groups with prefixes and nesting.
|
|
4
|
+
|
|
5
|
+
## Concepts
|
|
6
|
+
|
|
7
|
+
Controllers group related commands under a shared prefix, creating natural command hierarchies like `git remote add` or `docker compose up`. A controller is a class with `@Controller(prefix?)`. The prefix is prepended to all command paths inside it.
|
|
8
|
+
|
|
9
|
+
Controllers can be nested via `@ImportController()` — the child's prefix appends to the parent's, building multi-level command trees.
|
|
10
|
+
|
|
11
|
+
## API Reference
|
|
12
|
+
|
|
13
|
+
### `@Controller(prefix?: string)`
|
|
14
|
+
|
|
15
|
+
Class decorator (re-exported from `moost`). Marks a class as a CLI controller. The optional `prefix` sets a command prefix for all methods inside.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Cli, Controller, Param } from '@moostjs/event-cli'
|
|
19
|
+
|
|
20
|
+
@Controller('user')
|
|
21
|
+
export class UserController {
|
|
22
|
+
@Cli('create/:name')
|
|
23
|
+
create(@Param('name') name: string) {
|
|
24
|
+
return `Created user: ${name}`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@Cli('delete/:name')
|
|
28
|
+
delete(@Param('name') name: string) {
|
|
29
|
+
return `Deleted user: ${name}`
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
my-cli user create Alice
|
|
36
|
+
my-cli user delete Bob
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Without a prefix (`@Controller()`), commands are registered at the root level.
|
|
40
|
+
|
|
41
|
+
### `@ImportController(ctrl: Function)`
|
|
42
|
+
|
|
43
|
+
Class decorator (from `moost`). Nests a child controller inside the parent. The child's prefix appends to the parent's. Register only the top-level controller — imported children are registered automatically.
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { Cli, Controller, Param } from '@moostjs/event-cli'
|
|
47
|
+
import { ImportController } from 'moost'
|
|
48
|
+
|
|
49
|
+
@Controller('view')
|
|
50
|
+
export class ViewCommand {
|
|
51
|
+
@Cli(':id')
|
|
52
|
+
view(@Param('id') id: string) {
|
|
53
|
+
return `Viewing profile ${id}`
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
@Controller('profile')
|
|
58
|
+
@ImportController(ViewCommand)
|
|
59
|
+
export class ProfileController {
|
|
60
|
+
@Cli('list')
|
|
61
|
+
list() {
|
|
62
|
+
return 'Listing profiles...'
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
my-cli profile list # ProfileController.list()
|
|
69
|
+
my-cli profile view 42 # ViewCommand.view("42")
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Common Patterns
|
|
73
|
+
|
|
74
|
+
### Pattern: Registering controllers
|
|
75
|
+
|
|
76
|
+
With `CliApp`:
|
|
77
|
+
```ts
|
|
78
|
+
new CliApp()
|
|
79
|
+
.controllers(UserController, ConfigController)
|
|
80
|
+
.useHelp({ name: 'my-cli' })
|
|
81
|
+
.start()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
With standard Moost:
|
|
85
|
+
```ts
|
|
86
|
+
const app = new Moost()
|
|
87
|
+
app.registerControllers(UserController, ConfigController)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Pattern: Multi-level nesting
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
@Controller('add')
|
|
94
|
+
class RemoteAddController {
|
|
95
|
+
@Cli(':name/:url')
|
|
96
|
+
add(@Param('name') name: string, @Param('url') url: string) {
|
|
97
|
+
return `Adding remote ${name} -> ${url}`
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
@Controller('remote')
|
|
102
|
+
@ImportController(RemoteAddController)
|
|
103
|
+
class RemoteController {
|
|
104
|
+
@Cli('list')
|
|
105
|
+
list() { return 'Listing remotes...' }
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
@Controller()
|
|
109
|
+
@ImportController(RemoteController)
|
|
110
|
+
class GitController {
|
|
111
|
+
@Cli('status')
|
|
112
|
+
status() { return 'On branch main' }
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
my-cli status # GitController
|
|
118
|
+
my-cli remote list # RemoteController
|
|
119
|
+
my-cli remote add origin git@... # RemoteAddController
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Path composition
|
|
123
|
+
|
|
124
|
+
The final command path is: **controller prefix** + **child prefix** + **@Cli() path**.
|
|
125
|
+
|
|
126
|
+
| Parent prefix | Child prefix | `@Cli()` path | Final command |
|
|
127
|
+
|--------------|-------------|---------------|---------------|
|
|
128
|
+
| `''` (root) | — | `status` | `status` |
|
|
129
|
+
| `remote` | — | `list` | `remote list` |
|
|
130
|
+
| `remote` | `add` | `:name/:url` | `remote add :name :url` |
|
|
131
|
+
|
|
132
|
+
## Best Practices
|
|
133
|
+
|
|
134
|
+
- Use `@Controller(prefix)` to create command namespaces — `user`, `config`, `remote`
|
|
135
|
+
- Only register top-level controllers with `.controllers()` — nested children auto-register
|
|
136
|
+
- Keep nesting to 2-3 levels max for usability
|
|
137
|
+
- Use `@Controller()` (no prefix) for root-level commands
|
|
138
|
+
|
|
139
|
+
## Gotchas
|
|
140
|
+
|
|
141
|
+
- Registering a child controller directly AND via `@ImportController` can cause duplicate command registrations
|
|
142
|
+
- The controller prefix uses the same space/slash equivalence as command paths
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# Core concepts & setup — @moostjs/event-cli
|
|
2
|
+
|
|
3
|
+
> Installation, project scaffolding, and the mental model for building CLI apps with Moost.
|
|
4
|
+
|
|
5
|
+
## Concepts
|
|
6
|
+
|
|
7
|
+
`@moostjs/event-cli` is a Moost adapter that turns decorated TypeScript classes into CLI applications. It wraps `@wooksjs/event-cli` (the Wooks CLI engine) and integrates with Moost's decorator-driven DI, interceptors, and pipes.
|
|
8
|
+
|
|
9
|
+
The core mental model:
|
|
10
|
+
- **Commands** are methods decorated with `@Cli('path')` inside controller classes
|
|
11
|
+
- **Controllers** group commands under shared prefixes (like `git remote add`)
|
|
12
|
+
- **Options** are method parameters decorated with `@CliOption('flag')`
|
|
13
|
+
- **Arguments** are extracted from `:param` segments in command paths via `@Param('name')`
|
|
14
|
+
- **CliApp** is a convenience class that wires everything together with one fluent API
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @moostjs/event-cli moost @wooksjs/event-core wooks
|
|
20
|
+
# or
|
|
21
|
+
pnpm add @moostjs/event-cli moost @wooksjs/event-core wooks
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Peer dependencies: `moost`, `@wooksjs/event-core`, `wooks`.
|
|
25
|
+
|
|
26
|
+
## Project scaffolding
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm create moost -- --cli
|
|
30
|
+
# or with a name:
|
|
31
|
+
npm create moost my-cli -- --cli
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Creates:
|
|
35
|
+
```
|
|
36
|
+
my-cli/
|
|
37
|
+
├── src/
|
|
38
|
+
│ ├── controllers/
|
|
39
|
+
│ │ └── app.controller.ts
|
|
40
|
+
│ ├── main.ts
|
|
41
|
+
│ └── bin.ts
|
|
42
|
+
├── package.json
|
|
43
|
+
└── tsconfig.json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## CliApp — quick setup
|
|
47
|
+
|
|
48
|
+
`CliApp` extends `Moost` and provides a fluent API for the common case:
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { CliApp } from '@moostjs/event-cli'
|
|
52
|
+
import { AppController } from './controllers/app.controller'
|
|
53
|
+
|
|
54
|
+
new CliApp()
|
|
55
|
+
.controllers(AppController) // register controller classes
|
|
56
|
+
.useHelp({ name: 'my-cli' }) // enable --help with app name
|
|
57
|
+
.useOptions([ // global options shown in all commands
|
|
58
|
+
{ keys: ['help'], description: 'Display instructions for the command.' },
|
|
59
|
+
])
|
|
60
|
+
.start() // wire adapters + interceptors, call init()
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### `CliApp` methods
|
|
64
|
+
|
|
65
|
+
| Method | Description |
|
|
66
|
+
|--------|-------------|
|
|
67
|
+
| `.controllers(...ctrls)` | Register one or more controller classes (shortcut for `registerControllers`) |
|
|
68
|
+
| `.useHelp(opts)` | Configure the help system (name, title, colors, lookupLevel, maxWidth, maxLeft, mark) |
|
|
69
|
+
| `.useOptions(opts)` | Set global CLI options that appear in every command's help |
|
|
70
|
+
| `.start()` | Creates `MoostCli`, attaches adapter, applies help interceptor, calls `init()` |
|
|
71
|
+
|
|
72
|
+
## Exports
|
|
73
|
+
|
|
74
|
+
All imports come from `'@moostjs/event-cli'`:
|
|
75
|
+
|
|
76
|
+
| Export | Kind | Purpose |
|
|
77
|
+
|--------|------|---------|
|
|
78
|
+
| `CliApp` | class | Quick-setup CLI application factory |
|
|
79
|
+
| `MoostCli` | class | Low-level adapter (implements `TMoostAdapter`) |
|
|
80
|
+
| `Cli` | decorator | Define a CLI command on a method |
|
|
81
|
+
| `CliAlias` | decorator | Add an alias for a command |
|
|
82
|
+
| `CliExample` | decorator | Add a usage example for help output |
|
|
83
|
+
| `CliOption` | decorator | Bind a parameter to a `--flag` |
|
|
84
|
+
| `CliGlobalOption` | decorator | Define a global option on a controller class |
|
|
85
|
+
| `cliHelpInterceptor` | function | Factory for the help interceptor |
|
|
86
|
+
| `CliHelpInterceptor` | decorator | Decorator form of the help interceptor |
|
|
87
|
+
| `cliKind` | object | Event type identifier for CLI events |
|
|
88
|
+
| `Controller` | decorator | Re-exported from `moost` |
|
|
89
|
+
| `Param` | decorator | Re-exported from `moost` |
|
|
90
|
+
| `Intercept` | decorator | Re-exported from `moost` |
|
|
91
|
+
| `Description` | decorator | Re-exported from `moost` |
|
|
92
|
+
| `Optional` | decorator | Re-exported from `moost` |
|
|
93
|
+
| `defineBeforeInterceptor` | function | Re-exported from `moost` |
|
|
94
|
+
| `defineAfterInterceptor` | function | Re-exported from `moost` |
|
|
95
|
+
| `defineInterceptor` | function | Re-exported from `moost` |
|
|
96
|
+
| `TInterceptorPriority` | type | Re-exported from `moost` |
|
|
97
|
+
|
|
98
|
+
## Running the app
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Development
|
|
102
|
+
npx tsx src/bin.ts greet World
|
|
103
|
+
|
|
104
|
+
# Production (after compiling)
|
|
105
|
+
node dist/bin.js greet World
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
The return value of a command handler is printed to stdout.
|
|
109
|
+
|
|
110
|
+
## Best Practices
|
|
111
|
+
|
|
112
|
+
- Use `CliApp` for standard CLI apps; use `MoostCli` only when you need full control or multi-adapter setups
|
|
113
|
+
- Keep `tsconfig.json` with `"experimentalDecorators": true` and `"emitDecoratorMetadata": true`
|
|
114
|
+
- Register only top-level controllers — nested children are auto-registered via `@ImportController`
|