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.
Files changed (132) hide show
  1. package/README.md +66 -25
  2. package/dist/action/index.d.ts +2 -2
  3. package/dist/action/utils.d.ts +2 -2
  4. package/dist/actions/index.d.ts +2 -2
  5. package/dist/actions/types.d.ts +3 -3
  6. package/dist/actions/utils.d.ts +3 -3
  7. package/dist/app/index.d.ts +33 -87
  8. package/dist/app/types.d.ts +79 -26
  9. package/dist/boundary/components/broadcast/index.d.ts +2 -2
  10. package/dist/boundary/components/broadcast/types.d.ts +1 -1
  11. package/dist/boundary/components/consumer/components/partition/index.d.ts +1 -1
  12. package/dist/boundary/components/consumer/components/partition/types.d.ts +1 -1
  13. package/dist/boundary/components/consumer/index.d.ts +5 -5
  14. package/dist/boundary/components/consumer/types.d.ts +1 -1
  15. package/dist/boundary/components/consumer/utils.d.ts +1 -1
  16. package/dist/boundary/components/env/index.d.ts +3 -16
  17. package/dist/boundary/components/env/types.d.ts +24 -2
  18. package/dist/boundary/components/env/utils.d.ts +1 -1
  19. package/dist/boundary/components/scope/index.d.ts +2 -2
  20. package/dist/boundary/components/scope/types.d.ts +1 -1
  21. package/dist/boundary/components/scope/utils.d.ts +1 -1
  22. package/dist/boundary/components/sharing/index.d.ts +3 -3
  23. package/dist/boundary/components/tap/index.d.ts +3 -3
  24. package/dist/boundary/components/tap/types.d.ts +2 -2
  25. package/dist/boundary/components/tap/utils.d.ts +1 -1
  26. package/dist/boundary/components/tasks/index.d.ts +2 -2
  27. package/dist/boundary/components/tasks/utils.d.ts +1 -1
  28. package/dist/boundary/index.d.ts +3 -3
  29. package/dist/boundary/types.d.ts +3 -3
  30. package/dist/cache/index.d.ts +68 -12
  31. package/dist/cache/types.d.ts +33 -19
  32. package/dist/cli/bin/mh.js +10 -0
  33. package/dist/cli/lib/banner/index.js +14 -0
  34. package/dist/cli/lib/commands/app/index.js +37 -0
  35. package/dist/cli/lib/commands/feature/index.js +55 -0
  36. package/dist/cli/lib/commands/index.js +89 -0
  37. package/dist/cli/lib/commands/init/index.js +29 -0
  38. package/dist/cli/lib/commands/shared/index.js +56 -0
  39. package/dist/cli/lib/index.js +56 -0
  40. package/dist/cli/lib/parser/index.js +24 -0
  41. package/dist/cli/lib/prompt/index.js +61 -0
  42. package/dist/cli/lib/runner/index.js +46 -0
  43. package/dist/cli/lib/runner/types.js +1 -0
  44. package/dist/cli/lib/runner/utils.js +60 -0
  45. package/dist/cli/lib/types.js +1 -0
  46. package/dist/cli/lib/utils.js +20 -0
  47. package/dist/cli/templates/app/action/actions.ts.ejs.t +10 -0
  48. package/dist/cli/templates/app/action/types.ts.ejs.t +7 -0
  49. package/dist/cli/templates/app/integration/index.integration.tsx.ejs.t +13 -0
  50. package/dist/cli/templates/app/page/actions.ts.ejs.t +14 -0
  51. package/dist/cli/templates/app/page/index.tsx.ejs.t +20 -0
  52. package/dist/cli/templates/app/page/styles.ts.ejs.t +35 -0
  53. package/dist/cli/templates/app/page/types.ts.ejs.t +12 -0
  54. package/dist/cli/templates/feature/action/actions.ts.ejs.t +10 -0
  55. package/dist/cli/templates/feature/action/types.ts.ejs.t +7 -0
  56. package/dist/cli/templates/feature/multicast/types.ts.ejs.t +7 -0
  57. package/dist/cli/templates/feature/presentational/index.tsx.ejs.t +14 -0
  58. package/dist/cli/templates/feature/presentational/types.ts.ejs.t +12 -0
  59. package/dist/cli/templates/feature/presentational/utils.ts.ejs.t +8 -0
  60. package/dist/cli/templates/feature/stateful/actions.ts.ejs.t +16 -0
  61. package/dist/cli/templates/feature/stateful/index.tsx.ejs.t +19 -0
  62. package/dist/cli/templates/feature/stateful/types.ts.ejs.t +16 -0
  63. package/dist/cli/templates/feature/stateful/utils.ts.ejs.t +8 -0
  64. package/dist/cli/templates/feature/unit/index.test.tsx.ejs.t +21 -0
  65. package/dist/cli/templates/init/new/README.md.ejs.t +48 -0
  66. package/dist/cli/templates/init/new/eslint.config.js.ejs.t +88 -0
  67. package/dist/cli/templates/init/new/gitignore.ejs.t +9 -0
  68. package/dist/cli/templates/init/new/index.html.ejs.t +18 -0
  69. package/dist/cli/templates/init/new/package.json.ejs.t +54 -0
  70. package/dist/cli/templates/init/new/playwright.config.ts.ejs.t +17 -0
  71. package/dist/cli/templates/init/new/prettierrc.ejs.t +8 -0
  72. package/dist/cli/templates/init/new/src.app.index.tsx.ejs.t +14 -0
  73. package/dist/cli/templates/init/new/src.app.pages.home.actions.ts.ejs.t +16 -0
  74. package/dist/cli/templates/init/new/src.app.pages.home.index.tsx.ejs.t +30 -0
  75. package/dist/cli/templates/init/new/src.app.pages.home.integration.tsx.ejs.t +28 -0
  76. package/dist/cli/templates/init/new/src.app.pages.home.styles.ts.ejs.t +45 -0
  77. package/dist/cli/templates/init/new/src.app.pages.home.types.ts.ejs.t +12 -0
  78. package/dist/cli/templates/init/new/src.app.utils.ts.ejs.t +9 -0
  79. package/dist/cli/templates/init/new/src.features.greet.actions.ts.ejs.t +20 -0
  80. package/dist/cli/templates/init/new/src.features.greet.index.test.tsx.ejs.t +21 -0
  81. package/dist/cli/templates/init/new/src.features.greet.index.tsx.ejs.t +24 -0
  82. package/dist/cli/templates/init/new/src.features.greet.types.ts.ejs.t +18 -0
  83. package/dist/cli/templates/init/new/src.features.greet.utils.ts.ejs.t +8 -0
  84. package/dist/cli/templates/init/new/src.index.tsx.ejs.t +8 -0
  85. package/dist/cli/templates/init/new/src.shared.components.button.index.test.tsx.ejs.t +13 -0
  86. package/dist/cli/templates/init/new/src.shared.components.button.index.tsx.ejs.t +10 -0
  87. package/dist/cli/templates/init/new/src.shared.components.button.types.ts.ejs.t +6 -0
  88. package/dist/cli/templates/init/new/src.shared.resources.index.ts.ejs.t +4 -0
  89. package/dist/cli/templates/init/new/src.shared.theme.index.ts.ejs.t +51 -0
  90. package/dist/cli/templates/init/new/src.shared.types.index.ts.ejs.t +23 -0
  91. package/dist/cli/templates/init/new/src.test-setup.ts.ejs.t +10 -0
  92. package/dist/cli/templates/init/new/src.vite-env.d.ts.ejs.t +4 -0
  93. package/dist/cli/templates/init/new/tests.home.e2e.ts.ejs.t +14 -0
  94. package/dist/cli/templates/init/new/tsconfig.json.ejs.t +29 -0
  95. package/dist/cli/templates/init/new/vite.config.ts.ejs.t +17 -0
  96. package/dist/cli/templates/init/new/vitest.config.ts.ejs.t +24 -0
  97. package/dist/cli/templates/shared/component/index.tsx.ejs.t +9 -0
  98. package/dist/cli/templates/shared/component/types.ts.ejs.t +8 -0
  99. package/dist/cli/templates/shared/resource/index.ts.ejs.t +15 -0
  100. package/dist/cli/templates/shared/resource/types.ts.ejs.t +10 -0
  101. package/dist/cli/templates/shared/type-broadcast/types.ts.ejs.t +7 -0
  102. package/dist/cli/templates/shared/type-payload/types.ts.ejs.t +9 -0
  103. package/dist/cli/templates/shared/unit-component/index.test.tsx.ejs.t +13 -0
  104. package/dist/cli/templates/shared/unit-resource/index.test.ts.ejs.t +15 -0
  105. package/dist/cli/templates/shared/unit-util/index.test.ts.ejs.t +11 -0
  106. package/dist/cli/templates/shared/util/index.ts.ejs.t +6 -0
  107. package/dist/coalesce/index.d.ts +1 -1
  108. package/dist/context/index.d.ts +2 -2
  109. package/dist/error/index.d.ts +18 -1
  110. package/dist/error/types.d.ts +1 -18
  111. package/dist/error/utils.d.ts +1 -1
  112. package/dist/index.d.ts +16 -14
  113. package/dist/march-hare.js +7 -6
  114. package/dist/march-hare.js.map +1 -0
  115. package/dist/march-hare.umd.cjs +2 -1
  116. package/dist/march-hare.umd.cjs.map +1 -0
  117. package/dist/resource/index.d.ts +32 -61
  118. package/dist/resource/types.d.ts +45 -22
  119. package/dist/resource/utils.d.ts +31 -3
  120. package/dist/scope/index.d.ts +4 -64
  121. package/dist/scope/types.d.ts +8 -8
  122. package/dist/scope/utils.d.ts +12 -0
  123. package/dist/shared/index.d.ts +12 -21
  124. package/dist/types/index.d.ts +114 -29
  125. package/dist/utils/index.d.ts +3 -3
  126. package/dist/utils/types.d.ts +1 -3
  127. package/dist/utils/utils.d.ts +1 -3
  128. package/dist/with/index.d.ts +17 -62
  129. package/dist/with/types.d.ts +66 -0
  130. package/dist/with/utils.d.ts +61 -0
  131. package/package.json +21 -4
  132. 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.