march-hare 0.12.1 → 0.13.1
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 +66 -25
- package/dist/action/index.d.ts +2 -2
- package/dist/action/utils.d.ts +2 -2
- package/dist/actions/index.d.ts +2 -2
- package/dist/actions/types.d.ts +3 -3
- package/dist/actions/utils.d.ts +3 -3
- package/dist/app/index.d.ts +33 -87
- package/dist/app/types.d.ts +79 -26
- package/dist/boundary/components/broadcast/index.d.ts +2 -2
- package/dist/boundary/components/broadcast/types.d.ts +1 -1
- package/dist/boundary/components/consumer/components/partition/index.d.ts +1 -1
- package/dist/boundary/components/consumer/components/partition/types.d.ts +1 -1
- package/dist/boundary/components/consumer/index.d.ts +5 -5
- package/dist/boundary/components/consumer/types.d.ts +1 -1
- package/dist/boundary/components/consumer/utils.d.ts +1 -1
- package/dist/boundary/components/env/index.d.ts +3 -16
- package/dist/boundary/components/env/types.d.ts +24 -2
- package/dist/boundary/components/env/utils.d.ts +1 -1
- package/dist/boundary/components/scope/index.d.ts +2 -2
- package/dist/boundary/components/scope/types.d.ts +1 -1
- package/dist/boundary/components/scope/utils.d.ts +1 -1
- package/dist/boundary/components/sharing/index.d.ts +3 -3
- package/dist/boundary/components/tap/index.d.ts +3 -3
- package/dist/boundary/components/tap/types.d.ts +2 -2
- package/dist/boundary/components/tap/utils.d.ts +1 -1
- package/dist/boundary/components/tasks/index.d.ts +2 -2
- package/dist/boundary/components/tasks/utils.d.ts +1 -1
- package/dist/boundary/index.d.ts +3 -3
- package/dist/boundary/types.d.ts +3 -3
- package/dist/cache/index.d.ts +68 -12
- package/dist/cache/types.d.ts +33 -19
- package/dist/cli/bin/mh.js +10 -0
- package/dist/cli/lib/banner/index.js +14 -0
- package/dist/cli/lib/commands/app/index.js +37 -0
- package/dist/cli/lib/commands/feature/index.js +55 -0
- package/dist/cli/lib/commands/index.js +89 -0
- package/dist/cli/lib/commands/init/index.js +29 -0
- package/dist/cli/lib/commands/shared/index.js +56 -0
- package/dist/cli/lib/index.js +56 -0
- package/dist/cli/lib/parser/index.js +24 -0
- package/dist/cli/lib/prompt/index.js +61 -0
- package/dist/cli/lib/runner/index.js +46 -0
- package/dist/cli/lib/runner/types.js +1 -0
- package/dist/cli/lib/runner/utils.js +60 -0
- package/dist/cli/lib/types.js +1 -0
- package/dist/cli/lib/utils.js +20 -0
- package/dist/cli/templates/app/action/actions.ts.ejs.t +10 -0
- package/dist/cli/templates/app/action/types.ts.ejs.t +7 -0
- package/dist/cli/templates/app/integration/index.integration.tsx.ejs.t +13 -0
- package/dist/cli/templates/app/page/actions.ts.ejs.t +14 -0
- package/dist/cli/templates/app/page/index.tsx.ejs.t +20 -0
- package/dist/cli/templates/app/page/styles.ts.ejs.t +35 -0
- package/dist/cli/templates/app/page/types.ts.ejs.t +12 -0
- package/dist/cli/templates/feature/action/actions.ts.ejs.t +10 -0
- package/dist/cli/templates/feature/action/types.ts.ejs.t +7 -0
- package/dist/cli/templates/feature/multicast/types.ts.ejs.t +7 -0
- package/dist/cli/templates/feature/presentational/index.tsx.ejs.t +14 -0
- package/dist/cli/templates/feature/presentational/types.ts.ejs.t +12 -0
- package/dist/cli/templates/feature/presentational/utils.ts.ejs.t +8 -0
- package/dist/cli/templates/feature/stateful/actions.ts.ejs.t +16 -0
- package/dist/cli/templates/feature/stateful/index.tsx.ejs.t +19 -0
- package/dist/cli/templates/feature/stateful/types.ts.ejs.t +16 -0
- package/dist/cli/templates/feature/stateful/utils.ts.ejs.t +8 -0
- package/dist/cli/templates/feature/unit/index.test.tsx.ejs.t +21 -0
- package/dist/cli/templates/init/new/README.md.ejs.t +48 -0
- package/dist/cli/templates/init/new/eslint.config.js.ejs.t +88 -0
- package/dist/cli/templates/init/new/gitignore.ejs.t +9 -0
- package/dist/cli/templates/init/new/index.html.ejs.t +18 -0
- package/dist/cli/templates/init/new/package.json.ejs.t +54 -0
- package/dist/cli/templates/init/new/playwright.config.ts.ejs.t +17 -0
- package/dist/cli/templates/init/new/prettierrc.ejs.t +8 -0
- package/dist/cli/templates/init/new/src.app.index.tsx.ejs.t +14 -0
- package/dist/cli/templates/init/new/src.app.pages.home.actions.ts.ejs.t +16 -0
- package/dist/cli/templates/init/new/src.app.pages.home.index.tsx.ejs.t +30 -0
- package/dist/cli/templates/init/new/src.app.pages.home.integration.tsx.ejs.t +28 -0
- package/dist/cli/templates/init/new/src.app.pages.home.styles.ts.ejs.t +45 -0
- package/dist/cli/templates/init/new/src.app.pages.home.types.ts.ejs.t +12 -0
- package/dist/cli/templates/init/new/src.app.utils.ts.ejs.t +9 -0
- package/dist/cli/templates/init/new/src.features.greet.actions.ts.ejs.t +20 -0
- package/dist/cli/templates/init/new/src.features.greet.index.test.tsx.ejs.t +21 -0
- package/dist/cli/templates/init/new/src.features.greet.index.tsx.ejs.t +24 -0
- package/dist/cli/templates/init/new/src.features.greet.types.ts.ejs.t +18 -0
- package/dist/cli/templates/init/new/src.features.greet.utils.ts.ejs.t +8 -0
- package/dist/cli/templates/init/new/src.index.tsx.ejs.t +8 -0
- package/dist/cli/templates/init/new/src.shared.components.button.index.test.tsx.ejs.t +13 -0
- package/dist/cli/templates/init/new/src.shared.components.button.index.tsx.ejs.t +10 -0
- package/dist/cli/templates/init/new/src.shared.components.button.types.ts.ejs.t +6 -0
- package/dist/cli/templates/init/new/src.shared.resources.index.ts.ejs.t +4 -0
- package/dist/cli/templates/init/new/src.shared.theme.index.ts.ejs.t +51 -0
- package/dist/cli/templates/init/new/src.shared.types.index.ts.ejs.t +23 -0
- package/dist/cli/templates/init/new/src.test-setup.ts.ejs.t +10 -0
- package/dist/cli/templates/init/new/src.vite-env.d.ts.ejs.t +4 -0
- package/dist/cli/templates/init/new/tests.home.e2e.ts.ejs.t +14 -0
- package/dist/cli/templates/init/new/tsconfig.json.ejs.t +29 -0
- package/dist/cli/templates/init/new/vite.config.ts.ejs.t +17 -0
- package/dist/cli/templates/init/new/vitest.config.ts.ejs.t +24 -0
- package/dist/cli/templates/shared/component/index.tsx.ejs.t +9 -0
- package/dist/cli/templates/shared/component/types.ts.ejs.t +8 -0
- package/dist/cli/templates/shared/resource/index.ts.ejs.t +15 -0
- package/dist/cli/templates/shared/resource/types.ts.ejs.t +10 -0
- package/dist/cli/templates/shared/type-broadcast/types.ts.ejs.t +7 -0
- package/dist/cli/templates/shared/type-payload/types.ts.ejs.t +9 -0
- package/dist/cli/templates/shared/unit-component/index.test.tsx.ejs.t +13 -0
- package/dist/cli/templates/shared/unit-resource/index.test.ts.ejs.t +15 -0
- package/dist/cli/templates/shared/unit-util/index.test.ts.ejs.t +11 -0
- package/dist/cli/templates/shared/util/index.ts.ejs.t +6 -0
- package/dist/coalesce/index.d.ts +1 -1
- package/dist/context/index.d.ts +2 -2
- package/dist/error/index.d.ts +18 -1
- package/dist/error/types.d.ts +1 -18
- package/dist/error/utils.d.ts +1 -1
- package/dist/index.d.ts +16 -14
- package/dist/march-hare.js +7 -6
- package/dist/march-hare.js.map +1 -0
- package/dist/march-hare.umd.cjs +2 -1
- package/dist/march-hare.umd.cjs.map +1 -0
- package/dist/resource/index.d.ts +32 -61
- package/dist/resource/types.d.ts +45 -22
- package/dist/resource/utils.d.ts +31 -3
- package/dist/scope/index.d.ts +4 -64
- package/dist/scope/types.d.ts +8 -8
- package/dist/scope/utils.d.ts +12 -0
- package/dist/shared/index.d.ts +12 -21
- package/dist/types/index.d.ts +114 -29
- package/dist/utils/index.d.ts +3 -3
- package/dist/utils/types.d.ts +1 -3
- package/dist/utils/utils.d.ts +1 -3
- package/dist/with/index.d.ts +17 -62
- package/dist/with/types.d.ts +66 -0
- package/dist/with/utils.d.ts +61 -0
- package/package.json +21 -4
- package/src/cli/README.md +314 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
# `@march-hare/cli` — the `mh` scaffolder
|
|
2
|
+
|
|
3
|
+
A [Hygen](https://github.com/jondot/hygen)-style code generator that
|
|
4
|
+
scaffolds [March Hare](../../README.md) projects and the building
|
|
5
|
+
blocks inside them. Templates mirror the layout of
|
|
6
|
+
[`src/example/`](../example/) and the FSD layering rules enforced by
|
|
7
|
+
[`eslint-plugin-boundaries`](https://github.com/javierbrea/eslint-plugin-boundaries):
|
|
8
|
+
imports flow strictly downward (`app → features → shared`).
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
__ ___ __ __ __
|
|
12
|
+
/ |/ /___ ___________/ /_ / / / /___ _________
|
|
13
|
+
/ /|_/ / __ `/ ___/ ___/ __ \ / /_/ / __ `/ ___/ _ \
|
|
14
|
+
/ / / / /_/ / / / /__/ / / / / __ / /_/ / / / __/
|
|
15
|
+
/_/ /_/\__,_/_/ \___/_/ /_/ /_/ /_/\__,_/_/ \___/
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Contents
|
|
19
|
+
|
|
20
|
+
- [Quick start](#quick-start)
|
|
21
|
+
- [Interactive menu vs. direct commands](#interactive-menu-vs-direct-commands)
|
|
22
|
+
- [`mh init`](#mh-init)
|
|
23
|
+
- [`mh app …`](#mh-app-)
|
|
24
|
+
- [`mh feature …`](#mh-feature-)
|
|
25
|
+
- [`mh shared …`](#mh-shared-)
|
|
26
|
+
- [Generated project layout](#generated-project-layout)
|
|
27
|
+
- [How the templates work](#how-the-templates-work)
|
|
28
|
+
- [Adding your own generators](#adding-your-own-generators)
|
|
29
|
+
- [Development](#development)
|
|
30
|
+
|
|
31
|
+
## Quick start
|
|
32
|
+
|
|
33
|
+
The CLI ships with `march-hare` itself — installing the library
|
|
34
|
+
exposes the `mh` binary:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install -g march-hare # global: `mh` is on your PATH
|
|
38
|
+
mh init my-project
|
|
39
|
+
|
|
40
|
+
# — or —
|
|
41
|
+
|
|
42
|
+
npm install march-hare # local: invoke through npx / package scripts
|
|
43
|
+
npx mh init my-project
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Working inside this repo? Run the CLI straight from source — root
|
|
47
|
+
`yarn install` covers all its deps:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
node dist/cli/bin/mh.js # interactive menu
|
|
51
|
+
node dist/cli/bin/mh.js init demo # scaffold a project into ./demo
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
When run without arguments the CLI prints the Figlet banner and an
|
|
55
|
+
interactive menu of every top-level command.
|
|
56
|
+
|
|
57
|
+
## Interactive menu vs. direct commands
|
|
58
|
+
|
|
59
|
+
Every command in the CLI is a node in a tree. Pass none, one, or all
|
|
60
|
+
of the path segments — the CLI fills in the gaps with prompts.
|
|
61
|
+
|
|
62
|
+
| You type | What happens |
|
|
63
|
+
| -------------------- | -------------------------------------------------- |
|
|
64
|
+
| `mh` | Menu of top-level commands |
|
|
65
|
+
| `mh feature` | Menu of sub-commands under `feature` |
|
|
66
|
+
| `mh feature new` | Prompts you for the feature name then scaffolds it |
|
|
67
|
+
| `mh feature new foo` | Scaffolds `features/foo/` with no prompts |
|
|
68
|
+
| `mh --help` | Prints the whole command tree |
|
|
69
|
+
|
|
70
|
+
Any leaf command accepts `--name=value` and `--flag` style overrides
|
|
71
|
+
so you can drive it from scripts without prompts.
|
|
72
|
+
|
|
73
|
+
## `mh init`
|
|
74
|
+
|
|
75
|
+
Bootstraps a brand-new March Hare project mirroring `src/example/`.
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
mh init my-project
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Prompts you for:
|
|
82
|
+
|
|
83
|
+
- **Project name** — kebab-case slug used for the package name and the
|
|
84
|
+
folder it lives in.
|
|
85
|
+
- **Description** — populates `package.json` and the root README.
|
|
86
|
+
- **API base URL** — seeded into the App's Env at
|
|
87
|
+
`src/app/utils.ts`.
|
|
88
|
+
|
|
89
|
+
What you get:
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
my-project/
|
|
93
|
+
├── eslint.config.js ← FSD boundaries enforced via plugin-boundaries
|
|
94
|
+
├── package.json ← React 19 + March Hare 0.13 + Vite + Vitest + Playwright
|
|
95
|
+
├── playwright.config.ts
|
|
96
|
+
├── tsconfig.json ← @app/* @features/* @shared/* path aliases
|
|
97
|
+
├── vite.config.ts
|
|
98
|
+
├── vitest.config.ts
|
|
99
|
+
├── index.html
|
|
100
|
+
├── README.md
|
|
101
|
+
├── tests/
|
|
102
|
+
│ └── home.e2e.ts
|
|
103
|
+
└── src/
|
|
104
|
+
├── index.tsx
|
|
105
|
+
├── test-setup.ts
|
|
106
|
+
├── vite-env.d.ts
|
|
107
|
+
├── app/
|
|
108
|
+
│ ├── index.tsx ← <app.Boundary> root
|
|
109
|
+
│ ├── utils.ts ← App<Env.X>({ env: { apiBase } })
|
|
110
|
+
│ └── pages/home/ ← home page (button + greeting)
|
|
111
|
+
│ ├── index.tsx
|
|
112
|
+
│ ├── types.ts
|
|
113
|
+
│ ├── actions.ts
|
|
114
|
+
│ ├── styles.ts
|
|
115
|
+
│ └── index.integration.tsx
|
|
116
|
+
├── features/greet/ ← stateful "say hello" feature
|
|
117
|
+
│ ├── index.tsx
|
|
118
|
+
│ ├── types.ts
|
|
119
|
+
│ ├── actions.ts
|
|
120
|
+
│ ├── utils.ts ← shared.Scope<Envs, typeof Multicast>()
|
|
121
|
+
│ └── index.test.tsx
|
|
122
|
+
└── shared/
|
|
123
|
+
├── components/button/ ← antd wrapper + tests
|
|
124
|
+
├── theme/ ← colour / spacing / font / radius / shadow tokens
|
|
125
|
+
├── types/ ← Env namespace + Envs alias + Payload + Broadcast
|
|
126
|
+
└── resources/ ← empty barrel; add resources with `mh shared resource`
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
A working "Say hello" button is wired end-to-end so you can verify the
|
|
130
|
+
project boots before deleting it.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
cd my-project
|
|
134
|
+
yarn install # or npm install
|
|
135
|
+
yarn dev
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## `mh app …`
|
|
139
|
+
|
|
140
|
+
Manage the host layer.
|
|
141
|
+
|
|
142
|
+
| Command | What it does |
|
|
143
|
+
| ---------------------- | ------------------------------------------------------------------- |
|
|
144
|
+
| `mh app new <name>` | New page under `src/app/pages/<name>/` (index/types/actions/styles) |
|
|
145
|
+
| `mh app integration` | Picks a page, drops in an `index.integration.tsx` |
|
|
146
|
+
| `mh app action <page>` | Picks (or accepts) a page, injects a new `Actions.X` + handler |
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
mh app new dashboard --tagline="Live metrics"
|
|
152
|
+
mh app integration # prompts you to pick a page
|
|
153
|
+
mh app action dashboard Refresh # injects Actions.Refresh + a handler
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
The `action` command injects into both `types.ts` (adds
|
|
157
|
+
`static Refresh = Action("Refresh")`) and `actions.ts` (adds an empty
|
|
158
|
+
`actions.useAction(Actions.Refresh, …)` block before `return actions`).
|
|
159
|
+
Re-running the same command is a no-op thanks to `skip_if`.
|
|
160
|
+
|
|
161
|
+
## `mh feature …`
|
|
162
|
+
|
|
163
|
+
Manage feature slices.
|
|
164
|
+
|
|
165
|
+
| Command | What it does |
|
|
166
|
+
| -------------------------------- | ------------------------------------------------------------------------ |
|
|
167
|
+
| `mh feature new <name>` | New feature; asks whether it owns state (`--stateful` / `--no-stateful`) |
|
|
168
|
+
| `mh feature unit <name>` | Adds a `index.test.tsx` next to an existing feature |
|
|
169
|
+
| `mh feature action <feat> <act>` | Injects an `Actions.<Act>` member + a handler stub into the feature |
|
|
170
|
+
| `mh feature multicast <feat>` | Injects a multicast action into the feature's `Multicast` class |
|
|
171
|
+
|
|
172
|
+
Stateful features get four files: `index.tsx`, `types.ts`, `actions.ts`,
|
|
173
|
+
`utils.ts`. Presentational features get three (no `actions.ts`). Both
|
|
174
|
+
shapes include a `Multicast` class and a `scope` handle
|
|
175
|
+
(`shared.Scope<Envs, typeof Multicast>()`) — the structural contract
|
|
176
|
+
for the feature's private bus.
|
|
177
|
+
|
|
178
|
+
## `mh shared …`
|
|
179
|
+
|
|
180
|
+
Manage reusable building blocks.
|
|
181
|
+
|
|
182
|
+
| Command | What it does |
|
|
183
|
+
| --------------------------------- | ------------------------------------------------------------ |
|
|
184
|
+
| `mh shared component <name>` | New presentational atom under `src/shared/components/` |
|
|
185
|
+
| `mh shared resource <name>` | New `shared.Resource<Envs, T>` under `src/shared/resources/` |
|
|
186
|
+
| `mh shared util <name>` | New util module under `src/shared/utils/` |
|
|
187
|
+
| `mh shared type payload <name>` | Injects a `Payload.<Name>` type into `shared/types` |
|
|
188
|
+
| `mh shared type broadcast <name>` | Injects a global broadcast action into `shared/types` |
|
|
189
|
+
| `mh shared unit <kind> <name>` | Adds a unit test for a shared module |
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
mh shared component card
|
|
193
|
+
mh shared resource user
|
|
194
|
+
mh shared util parse-date
|
|
195
|
+
mh shared type payload Notification
|
|
196
|
+
mh shared type broadcast Toast
|
|
197
|
+
mh shared unit components card
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
After creating a resource, re-export it from
|
|
201
|
+
`src/shared/resources/index.ts` so it stays reachable as
|
|
202
|
+
`resource.<name>.fetch()`:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
export * as user from "./user/index.ts";
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Generated project layout
|
|
209
|
+
|
|
210
|
+
The CLI's `init` template seeds the App's `Env` shape based on the
|
|
211
|
+
project name. If you run `mh init billing`, the generated
|
|
212
|
+
`shared/types/index.ts` declares `Env.Billing` and `Envs = Env.Billing`:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
export namespace Env {
|
|
216
|
+
export type Billing = {
|
|
217
|
+
apiBase: string;
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export type Envs = Env.Billing;
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
When you later add a second deployable (e.g. a marketing site) you
|
|
225
|
+
widen `Envs` to a union and add a [user-defined type
|
|
226
|
+
guard](https://www.typescriptlang.org/docs/handbook/advanced-types.html)
|
|
227
|
+
to narrow at the call site — see the example's
|
|
228
|
+
[README](../example/README.md) for the full pattern.
|
|
229
|
+
|
|
230
|
+
## How the templates work
|
|
231
|
+
|
|
232
|
+
Templates live under [`templates/<generator>/<action>/`](./templates).
|
|
233
|
+
Each `.ejs.t` file has a tiny [Hygen-style](https://www.hygen.io/docs/templates)
|
|
234
|
+
frontmatter block plus an EJS body.
|
|
235
|
+
|
|
236
|
+
```ejs
|
|
237
|
+
---
|
|
238
|
+
to: src/features/<%= name %>/index.tsx
|
|
239
|
+
---
|
|
240
|
+
import * as React from "react";
|
|
241
|
+
// …
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Supported frontmatter keys:
|
|
245
|
+
|
|
246
|
+
| Key | What it does |
|
|
247
|
+
| --------- | ------------------------------------------------------------------------------------------ |
|
|
248
|
+
| `to` | Output path. Rendered through EJS so it can reference template vars. |
|
|
249
|
+
| `inject` | Set to `true` to inject the body into an existing file instead of overwriting it. |
|
|
250
|
+
| `before` | Regex (multiline). With `inject`, inserts the body **above** the first match. |
|
|
251
|
+
| `after` | Regex (multiline). With `inject`, inserts the body **below** the first match. |
|
|
252
|
+
| `skip_if` | Regex (multiline). With `inject`, skip the file if it already matches (idempotent reruns). |
|
|
253
|
+
| `if` | EJS expression. Skip the template entirely unless it evaluates truthy. |
|
|
254
|
+
| `force` | EJS expression. When truthy, overwrite an existing file (default: skip). |
|
|
255
|
+
|
|
256
|
+
Helpers available inside templates:
|
|
257
|
+
|
|
258
|
+
| Helper | Example | Result |
|
|
259
|
+
| ----------- | ------------------- | ----------- |
|
|
260
|
+
| `kebab(s)` | `kebab("AddCat")` | `"add-cat"` |
|
|
261
|
+
| `pascal(s)` | `pascal("add-cat")` | `"AddCat"` |
|
|
262
|
+
| `camel(s)` | `camel("add-cat")` | `"addCat"` |
|
|
263
|
+
| `title(s)` | `title("add-cat")` | `"Add Cat"` |
|
|
264
|
+
|
|
265
|
+
Standard variables passed to every render:
|
|
266
|
+
|
|
267
|
+
- `name` — the kebab-case name accepted from positional / prompt.
|
|
268
|
+
- `pascalName` — same value run through `pascal()`.
|
|
269
|
+
|
|
270
|
+
Commands pass additional vars (e.g. `feature`, `page`, `rawName`,
|
|
271
|
+
`env`, `apiBase`) — see the individual command files under
|
|
272
|
+
[`lib/commands/`](./lib/commands).
|
|
273
|
+
|
|
274
|
+
## Adding your own generators
|
|
275
|
+
|
|
276
|
+
1. Create `templates/<generator>/<action>/foo.ejs.t` with a `to:`
|
|
277
|
+
frontmatter and an EJS body.
|
|
278
|
+
2. Wire a leaf into [`lib/commands/index.ts`](./lib/commands/index.ts) that calls
|
|
279
|
+
`scaffold("<generator>", "<action>", vars, { cwd })`.
|
|
280
|
+
3. Optionally add prompts via the helpers in
|
|
281
|
+
[`lib/prompt/index.ts`](./lib/prompt/index.ts).
|
|
282
|
+
|
|
283
|
+
Templates can render any number of files — every `.ejs.t` under the
|
|
284
|
+
action directory is processed, with each `to:` resolved
|
|
285
|
+
independently. Use nested directories under the action to keep big
|
|
286
|
+
generators (like `init`) tidy.
|
|
287
|
+
|
|
288
|
+
## Development
|
|
289
|
+
|
|
290
|
+
The CLI's runtime deps (`@inquirer/prompts`, `ejs`, `figlet`, `kleur`)
|
|
291
|
+
live in the root [`package.json`](../../package.json). A single
|
|
292
|
+
`yarn install` at the repo root is all you need:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
yarn install # from the repo root
|
|
296
|
+
node dist/cli/bin/mh.js # run the CLI from source
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
There are no build steps — the CLI is plain ESM JavaScript. Templates
|
|
300
|
+
are read directly from disk at runtime.
|
|
301
|
+
|
|
302
|
+
To verify a change end-to-end:
|
|
303
|
+
|
|
304
|
+
```bash
|
|
305
|
+
cd /tmp && rm -rf check && mkdir check && cd check
|
|
306
|
+
node "$OLDPWD/dist/cli/bin/mh.js" init demo \
|
|
307
|
+
--description="dev check" --apiBase="https://api.example.test"
|
|
308
|
+
cd demo
|
|
309
|
+
node /Users/adamtimberlake/Webroot/MarchHare/dist/cli/bin/mh.js feature new counter --stateful
|
|
310
|
+
node /Users/adamtimberlake/Webroot/MarchHare/dist/cli/bin/mh.js feature action counter Reset
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Then `yarn install && yarn checks` inside the generated project should
|
|
314
|
+
pass cleanly.
|