formstand 0.7.0 → 0.9.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/CHANGELOG.md +363 -0
- package/README.md +2 -1
- package/dist/index.cjs +274 -45
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +61 -1
- package/dist/index.d.ts +61 -1
- package/dist/index.js +269 -46
- package/dist/index.js.map +1 -1
- package/package.json +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,368 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.9.0 — 2026-07-10
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `validateFields` no longer fabricates a "required" error for a field
|
|
10
|
+
whose required ancestor is `undefined` (the field-scoped fast path parsed
|
|
11
|
+
the leaf against undefined where the full-form parse keys the error at the
|
|
12
|
+
ancestor) — sync and async agreed again. `slotAtPath` now checks the
|
|
13
|
+
container at each step.
|
|
14
|
+
- `useVariantField` keeps its type guard for **optional/nullable**
|
|
15
|
+
discriminated unions (a missing `NonNullable` let the nullish member
|
|
16
|
+
collapse `keyof` to `never` and leak every key, including the
|
|
17
|
+
discriminant).
|
|
18
|
+
- `devtools` is active only in non-production builds
|
|
19
|
+
(`NODE_ENV !== "production"`), so opting in during development never
|
|
20
|
+
streams a shipped form's state to an end user with the extension.
|
|
21
|
+
- `parseDateText` accepts calendar years under 100 (the Date constructor
|
|
22
|
+
maps 0–99 to 1900–1999; the literal year is forced back on).
|
|
23
|
+
- `DateField` / `dateInputProps` preserve the existing value's time-of-day,
|
|
24
|
+
so re-picking the same day on a timestamped value is no longer spuriously
|
|
25
|
+
dirty (and changing the day keeps the time).
|
|
26
|
+
- `parsePath`'s cached segment array is frozen — it is shared across callers
|
|
27
|
+
now, so external mutation throws instead of corrupting the cache.
|
|
28
|
+
- `useVariantField` docs the write-gating contract (gate variant-field
|
|
29
|
+
writes on the discriminant, like rendering).
|
|
30
|
+
|
|
31
|
+
### Added
|
|
32
|
+
|
|
33
|
+
- `useVariantField(form, unionPath, field)`: typed access to the
|
|
34
|
+
variant-specific fields of a `z.discriminatedUnion` — the ones
|
|
35
|
+
`FieldPath` omits because they exist in only some union members (the
|
|
36
|
+
discriminant stays a common key, bound with plain `useField`). Typed as
|
|
37
|
+
the field's value across the variants that declare it, widened with
|
|
38
|
+
`| undefined`; a non-variant field or the discriminant itself is a
|
|
39
|
+
compile error. `createFormHooks` gains a bound `use{Name}VariantField`.
|
|
40
|
+
- `persistForm(form, { key })`: the autosave recipe as a first-class
|
|
41
|
+
helper — debounced JSON drafts to any `{ getItem, setItem, removeItem }`
|
|
42
|
+
storage, draft auto-apply on start (`adopt` rebases clean, `restore`
|
|
43
|
+
loads dirty, `manual` waits for you), `clear()` that also cancels
|
|
44
|
+
pending writes, and guarded storage access throughout.
|
|
45
|
+
- Redux DevTools: `createForm(schema, { devtools: "checkout" })` connects
|
|
46
|
+
the form's store to the extension via zustand's middleware — every write
|
|
47
|
+
named, inspectable, and time-travelable. Off by default and inert
|
|
48
|
+
without the extension.
|
|
49
|
+
- Hot-path performance (the 2026-07 review's efficiency findings, no API
|
|
50
|
+
changes): `parsePath` is memoized (every field subscription re-parsed
|
|
51
|
+
its path per store notification); `validateFields` parses just the
|
|
52
|
+
requested subschemas when they're extractable — cross-field rules still
|
|
53
|
+
force the full parse, because extraction bails on refined levels — so a
|
|
54
|
+
wizard step stops paying for the whole schema per click; `diff()`/
|
|
55
|
+
`dirtyFields()` deep-compare each node once instead of once per ancestor
|
|
56
|
+
level; `focusFirstError` runs its focusability DOM walks only on
|
|
57
|
+
name-matched candidates instead of every named control in the document.
|
|
58
|
+
- Dates are a first-class field kind: `DateField` (an `<input type="date">`
|
|
59
|
+
bound to Date-typed paths) joins the shipped components, with
|
|
60
|
+
`dateInputProps` / `dateToInputText` / `parseDateText` exported for
|
|
61
|
+
custom markup and UI-kit adapters. Local calendar-date semantics
|
|
62
|
+
throughout (never `toISOString()` — June 1 stays June 1 west of UTC),
|
|
63
|
+
the `emptyValue` round-trip on clear, and rollover rejection
|
|
64
|
+
(`2026-02-31` is invalid, not March 3).
|
|
65
|
+
|
|
66
|
+
### CI (no package changes)
|
|
67
|
+
|
|
68
|
+
- Dependabot triage: esbuild pinned to the patched 0.28.1 line via npm
|
|
69
|
+
overrides in the root and cli (dev-server advisories), scoped to spare
|
|
70
|
+
vitepress 1.x's nested vite-5 toolchain (esbuild 0.28 can't downlevel to
|
|
71
|
+
vite 5's browser targets). The remaining vitepress-nested alerts are
|
|
72
|
+
dismissed as tolerable risk — local docs dev/build only — with the
|
|
73
|
+
VitePress 2 migration tracked on the roadmap as the real fix.
|
|
74
|
+
- Workflows declare least-privilege GITHUB_TOKEN permissions (CodeQL:
|
|
75
|
+
actions/missing-workflow-permissions).
|
|
76
|
+
|
|
77
|
+
### Docs & examples (no package changes)
|
|
78
|
+
|
|
79
|
+
- The Schema builder gains a **Paste a TypeScript type** mode: paste an
|
|
80
|
+
interface or type alias and the same in-browser emitters generate the
|
|
81
|
+
form — no TypeScript compiler in the bundle, a focused ~250-line parser
|
|
82
|
+
covers the CLI's type-mode subset (string/number/boolean/Date, arrays,
|
|
83
|
+
nested objects, string-literal unions, optional/nullable) and degrades
|
|
84
|
+
the rest to a text field with a TODO. Both input modes share one emit
|
|
85
|
+
path.
|
|
86
|
+
- ROADMAP.md: a living plan for the library and the CLI — the 0.9/cli-0.6
|
|
87
|
+
cycle (dates as a first-class field kind, hot-path performance,
|
|
88
|
+
DevTools, CLI config + watch), the next tier, the parking lot, and the
|
|
89
|
+
review's deferred cleanup items.
|
|
90
|
+
- The theme is one preference across the docs site and the playground:
|
|
91
|
+
the playground stores its light/dark choice under VitePress's own
|
|
92
|
+
localStorage key (same origin), so flipping either surface's toggle
|
|
93
|
+
follows you to the other.
|
|
94
|
+
- The demo body caps at a readable 760px measure; legitimately wide demos
|
|
95
|
+
(Perf's grid, the Schema builder, the Invoice table) opt out via an
|
|
96
|
+
explicit WIDE_DEMOS set.
|
|
97
|
+
- The demo card gets a real header: group eyebrow, title, a one-line
|
|
98
|
+
description of what each demo shows (exhaustive over the tab keys, like
|
|
99
|
+
the group map), and the View state / View code toggles plus a copy-link
|
|
100
|
+
button as a right-aligned action cluster — shell chrome separated from
|
|
101
|
+
demo content by a hairline. The nav groups show demo counts, and the
|
|
102
|
+
mobile bottom sheet gains its own Close button (the header toggles sit
|
|
103
|
+
behind it).
|
|
104
|
+
|
|
105
|
+
- Mobile playground shell: a top app bar (menu / title / controls), the
|
|
106
|
+
demo list as an off-canvas drawer that closes on pick, and the View
|
|
107
|
+
state / View code panel as a bottom sheet — all CSS-driven, desktop
|
|
108
|
+
layout unchanged.
|
|
109
|
+
- Light theme: a toggle in the app bar (and sidebar on desktop), defaulting
|
|
110
|
+
to the OS preference and persisted per browser. One variable palette in
|
|
111
|
+
styles.css drives the shell; the MUI bridge and the shadcn scope follow
|
|
112
|
+
the same html[data-theme] switch. Terminal/code surfaces deliberately
|
|
113
|
+
stay dark in both themes.
|
|
114
|
+
- The GitHub link in the shell shows the repo's star count (best-effort,
|
|
115
|
+
cached per session, skipped under tests).
|
|
116
|
+
|
|
117
|
+
## formstand-cli 0.6.0 — 2026-07-10
|
|
118
|
+
|
|
119
|
+
- Discriminated unions (`z.discriminatedUnion`) at a field position: the
|
|
120
|
+
generator emits a discriminant select plus per-variant conditional
|
|
121
|
+
blocks, binding variant fields through the library's `useVariantField`
|
|
122
|
+
(single-file) or bound `use{Name}VariantField` (module layout) — fully
|
|
123
|
+
typed, no casts. All six emit paths (plain/mui/shadcn × single-file/
|
|
124
|
+
module) typecheck against the real helper. Initial values pick the
|
|
125
|
+
first variant; the schema round-trips. Nested-in-section unions,
|
|
126
|
+
unions in array rows, and non-scalar variant fields keep a TODO.
|
|
127
|
+
- Nested-array row extraction (`--layout module`): an array inside a
|
|
128
|
+
section object, or an array inside an array row, now generates its own
|
|
129
|
+
Row/Rows component pair with a bound `useFieldArray` on the template
|
|
130
|
+
path (two numeric holes for arrays-in-rows,
|
|
131
|
+
`contacts.${parentIndex}.phones.${index}.number`) instead of a TODO
|
|
132
|
+
comment. One extraction level per array — deeper nesting keeps a TODO.
|
|
133
|
+
- `formstand.config.ts` (also `.mts`/`.js`/`.mjs`, or `--config <file>`):
|
|
134
|
+
project defaults for `ui`/`layout`/`sections`/`columns`, written with the
|
|
135
|
+
typed `defineConfig` export. Precedence is flags > config > built-ins,
|
|
136
|
+
and config typos fail as loudly as flag typos.
|
|
137
|
+
- `--watch`: regenerate whenever the input file changes (requires
|
|
138
|
+
`--out`; reruns overwrite their own output). Watches the parent
|
|
139
|
+
directory so editors that save via rename don't kill the watcher.
|
|
140
|
+
- A kind × optional × nullable matrix test pins `emitInitialValues` and
|
|
141
|
+
`blankNeedsCast` agreement in both directions: the no-cast combos must
|
|
142
|
+
typecheck with the checked annotation, and every cast combo is proven
|
|
143
|
+
to genuinely need it.
|
|
144
|
+
- Date fields emit real bindings instead of a text input with a cast and
|
|
145
|
+
a TODO: `DateField` (plain), `muiDateFieldProps` (MUI `type="date"`
|
|
146
|
+
TextField with a floated label), `shadcnDateInputProps` (shadcn Input).
|
|
147
|
+
`DATE_CAST` and the per-backend date TODOs are gone from generated
|
|
148
|
+
output — forms with dates submit successfully out of the box. Requires
|
|
149
|
+
formstand ≥ 0.9 for date fields.
|
|
150
|
+
|
|
151
|
+
## 0.8.0 — 2026-07-10
|
|
152
|
+
|
|
153
|
+
#### Fixed (2026-07 full-repo review)
|
|
154
|
+
|
|
155
|
+
- `valuesEqual` compared only the first object's keys, so two objects with
|
|
156
|
+
equal key counts but different key sets (one holding `undefined`) read as
|
|
157
|
+
equal — dirty flags, `diff()`, and server-error release could all treat a
|
|
158
|
+
real change as clean. Key sets must now match (`Object.hasOwn`), and the
|
|
159
|
+
compare is symmetric.
|
|
160
|
+
- `setAtPath` no longer spreads a `Date`/`Map`/`Set`/class instance at an
|
|
161
|
+
intermediate path into a plain object (silently destroying it); the write
|
|
162
|
+
is refused with a console warning, like other unwritable shapes.
|
|
163
|
+
- Path reads return own properties only: `getAtPath(values,
|
|
164
|
+
"lookup.constructor")` on a `z.record` is `undefined` instead of leaking
|
|
165
|
+
`Object.prototype` members as field values.
|
|
166
|
+
- A throwing `onInvalid` handler now resolves `submit()` with
|
|
167
|
+
`{ kind: "error" }` like a throwing `onValid`, instead of rejecting out of
|
|
168
|
+
a DOM event handler.
|
|
169
|
+
- `restore()` no longer resurrects a snapshotted `isSubmitting` — the flag
|
|
170
|
+
reflects live submit passes, so a mid-submit snapshot restored later can't
|
|
171
|
+
disable submit buttons forever (same rule the validation flags already
|
|
172
|
+
followed).
|
|
173
|
+
- `SelectField` on a nullable field keeps its empty option visible and
|
|
174
|
+
selectable after a choice, so the field can be cleared back to `null`
|
|
175
|
+
through the UI (the `emptyValue` round-trip `selectProps` always
|
|
176
|
+
supported). Non-nullable fields keep the disabled placeholder.
|
|
177
|
+
- `useFieldArray` no longer hands a removed row's id to a row appended in
|
|
178
|
+
the same update (React treated it as a reorder and resurrected the dead
|
|
179
|
+
row's DOM state); the same-index fallback still keeps an edited row's id.
|
|
180
|
+
- `useForm` now also warns when the CONTENT of `initialValues` changes after
|
|
181
|
+
mount (the async-fetch pattern with a module-hoisted schema — previously
|
|
182
|
+
silently ignored with no warning at all). Inline literals with identical
|
|
183
|
+
content stay silent.
|
|
184
|
+
- Docs: `field-arrays.md` claimed array ops re-key `isValidating`; they
|
|
185
|
+
deliberately drop in-flight flags (the pass that set them is stale) — the
|
|
186
|
+
guide now says so. `components.md` documents the `checkboxProps`
|
|
187
|
+
unchecked-writes-`false` tradeoff for nullable booleans.
|
|
188
|
+
|
|
189
|
+
### Docs & examples (no package changes)
|
|
190
|
+
|
|
191
|
+
- The docs nav gains a "Built on" menu and the footer links to
|
|
192
|
+
[zod](https://zod.dev) and [zustand](https://zustand.docs.pmnd.rs).
|
|
193
|
+
|
|
194
|
+
- A "Schema builder" tab: formstand-gen running in the browser. A formstand
|
|
195
|
+
form describes a schema (fields, sections, arrays, enum options — with
|
|
196
|
+
duplicate-name and enum-needs-options cross-field validation), its values
|
|
197
|
+
map straight onto the CLI's IR, and the REAL emitters (imported from
|
|
198
|
+
cli/src — pure string builders, no Node APIs) regenerate the output files
|
|
199
|
+
on every keystroke, shown in a file tree with copy-file / copy-all. All
|
|
200
|
+
the CLI axes are selects: --ui, --layout, --sections, --columns.
|
|
201
|
+
- The Schema builder's "Copy all files" is now "Download .zip" — a
|
|
202
|
+
dependency-free STORE-only zip writer (~50 lines, in the demo's own
|
|
203
|
+
source) bundles the generated files under a folder named for the
|
|
204
|
+
component, exactly the tree `--out` would have written.
|
|
205
|
+
- Every demo has a direct link: hash routes like
|
|
206
|
+
`examples/#/schema-builder` (kebab-cased tab keys), synced both ways so
|
|
207
|
+
back/forward and hand-edited URLs work. Hash routing because GitHub Pages
|
|
208
|
+
is static hosting.
|
|
209
|
+
- The CLI command builder gains Sections and Field columns selects, and the
|
|
210
|
+
Generated Onboarding tab now showcases `--sections panel --columns 2`
|
|
211
|
+
(still CI-verified as the CLI's untouched output).
|
|
212
|
+
|
|
213
|
+
- A "CLI command builder" tab: a formstand form (cross-field superRefine,
|
|
214
|
+
conditional fields) whose live-updating formstand-gen command line is
|
|
215
|
+
useFormSelector-derived state, with a copy button.
|
|
216
|
+
- A "Generated" playground group whose Onboarding tab is formstand-gen's
|
|
217
|
+
untouched `--layout module --ui mui` output for the Onboarding schema —
|
|
218
|
+
CI regenerates it and fails on drift, so the demo is provably what the
|
|
219
|
+
current CLI emits (scripts/generate-cli-demos.mjs).
|
|
220
|
+
|
|
221
|
+
- MUI and shadcn variants of the Onboarding feature module (31 demos now):
|
|
222
|
+
same shared schema, option lists, and blank draft as the plain module
|
|
223
|
+
(re-exported, so the three can't drift), each with its own form instance
|
|
224
|
+
and pre-wired hooks — MUI sections are Accordions with dirty/valid chips,
|
|
225
|
+
shadcn sections are collapsible with badges. The CLI generates the same
|
|
226
|
+
architecture from the same schema (`--layout module --ui mui|shadcn`).
|
|
227
|
+
- The sidebar demo list is a tree view (x-tree-view): brass group folders,
|
|
228
|
+
form-check leaf icons, brass selection.
|
|
229
|
+
|
|
230
|
+
- The playground gets a real app layout: grouped demo navigation in a
|
|
231
|
+
sidebar (Core / Patterns / Material UI / shadcn/ui — the group map is
|
|
232
|
+
exhaustive over the tab keys, so an unassigned demo is a compile error),
|
|
233
|
+
a sticky scrollable sidebar from tablet width up, and responsive tiers
|
|
234
|
+
through HD to 4K; on mobile the nav becomes wrapping chips above the
|
|
235
|
+
content. `/guide/` on the docs site now lands on a guide index instead
|
|
236
|
+
of a 404.
|
|
237
|
+
- Fixed: clicking a checkbox's label made the field and everything below
|
|
238
|
+
it jump down for a frame and snap back (Chrome's label-forwarded
|
|
239
|
+
activation briefly rendered the checkbox as a padded text-input box —
|
|
240
|
+
reproduced and verified fixed in real Chromium via Playwright). The
|
|
241
|
+
playground's text-input chrome now excludes checkboxes, which also gain
|
|
242
|
+
a brass accent-color and a focus-visible ring.
|
|
243
|
+
- View state / View code move to a panel on the demo's right (stacked
|
|
244
|
+
below on narrow screens), toggled by the same two buttons; multi-file
|
|
245
|
+
demos (Onboarding) get a file-tree navigator (@mui/x-tree-view with
|
|
246
|
+
folder/file icons) opening on hooks.ts; the sidebar brand links back to
|
|
247
|
+
the docs and stays pinned while the demo list scrolls.
|
|
248
|
+
- The Perf demo registers its form like every other tab, so View state
|
|
249
|
+
works (the shell only mounts the panel while open, so a closed panel
|
|
250
|
+
costs the benchmark nothing — the copy says to close it while running).
|
|
251
|
+
The benchmark writes `field0` instead of the mid-grid field, so the
|
|
252
|
+
change is visible without scrolling.
|
|
253
|
+
|
|
254
|
+
## formstand-cli 0.5.3 — 2026-07-10
|
|
255
|
+
|
|
256
|
+
### Fixed
|
|
257
|
+
|
|
258
|
+
- Emitted string literals escape U+2028/U+2029 (JSON.stringify leaves the
|
|
259
|
+
JS line separators raw — a syntax error for pre-ES2019 parsers of the
|
|
260
|
+
GENERATED file, and CodeQL's js/bad-code-sanitization).
|
|
261
|
+
|
|
262
|
+
## formstand-cli 0.5.2 — 2026-07-10
|
|
263
|
+
|
|
264
|
+
#### Fixed (2026-07 full-repo review)
|
|
265
|
+
|
|
266
|
+
- Digit-leading schema keys ("2fa", "2ndOwners") emitted invalid
|
|
267
|
+
identifiers (`const 2ndOwnersArray`); pascalCase/camelCase results are
|
|
268
|
+
now underscore-prefixed when they'd start with a digit.
|
|
269
|
+
- Array-row fields named JS reserved words ("new", "delete") emitted
|
|
270
|
+
reserved-word `const` declarations in module sections; row bindings now
|
|
271
|
+
use an identifier-safe variant (`new_`).
|
|
272
|
+
- A field named after the module prefix (field "contact" in ContactForm)
|
|
273
|
+
collided with the bound `useContactField` hook from `hooks.ts` — a
|
|
274
|
+
duplicate declaration plus self-recursion; component names are now
|
|
275
|
+
deduped against the hook names too (`ContactField2`).
|
|
276
|
+
- Boolean-only schemas with `--ui mui --layout module` emitted an
|
|
277
|
+
`adapter.ts` using `ChangeEvent` without importing it.
|
|
278
|
+
- `--name` is validated as an identifier (reserved words rejected) instead
|
|
279
|
+
of interpolating verbatim into declarations and file names.
|
|
280
|
+
- Cross-drive input/`--out` on Windows produced an unresolvable
|
|
281
|
+
`"./D:/..."` import specifier; the CLI now fails loudly at generation
|
|
282
|
+
time.
|
|
283
|
+
- zod's `.nonoptional()` unwrapped transparently, letting an inner
|
|
284
|
+
`.optional()` win — the field's checked `initialValues` annotation then
|
|
285
|
+
failed to typecheck; `.nonoptional()` now re-requires the field.
|
|
286
|
+
- A field literally named `__proto__` silently vanished from emitted object
|
|
287
|
+
literals (prototype-setter semantics); it's now emitted as a computed key.
|
|
288
|
+
- `--sections panel` with `--ui mui` rendered different chrome per
|
|
289
|
+
`--layout` (single-file used `CardHeader`, module used `Typography` in
|
|
290
|
+
`CardContent`); both emitters now share the module shape, the grid
|
|
291
|
+
strings come from one set of helpers instead of six inline copies, and
|
|
292
|
+
`emitInitialValues`/`blankNeedsCast` read one shared blank-value table so
|
|
293
|
+
they can't drift.
|
|
294
|
+
|
|
295
|
+
## formstand-cli 0.5.1 — 2026-07-09
|
|
296
|
+
|
|
297
|
+
### Fixed
|
|
298
|
+
|
|
299
|
+
- Generated `initialValues` (and array empty-item constants) only use the
|
|
300
|
+
`as unknown as` escape hatch when the blank draft genuinely can't
|
|
301
|
+
typecheck (a required number/date/enum starts `undefined`); every other
|
|
302
|
+
schema gets a checked type annotation, so typos in edited initial values
|
|
303
|
+
are compile errors again.
|
|
304
|
+
- `--layout module`: the generated `index.ts` now re-exports `./schema` and
|
|
305
|
+
`./types` alongside `./hooks`, so consumers can type submit handlers and
|
|
306
|
+
server code (`ProfileValues`, `profileSchema`) off the module's public
|
|
307
|
+
API instead of deep-importing its files.
|
|
308
|
+
|
|
309
|
+
## formstand-cli 0.5.0 — 2026-07-09
|
|
310
|
+
|
|
311
|
+
### Added
|
|
312
|
+
|
|
313
|
+
- `--sections flat|panel|collapsible` and `--columns 1|2|3`: minimal visual
|
|
314
|
+
layout options, working with both `--layout single` and `--layout module`
|
|
315
|
+
and all three uis. Sections render as flat headings (default, unchanged
|
|
316
|
+
output), bordered panels, or collapsible sections; fields inside each
|
|
317
|
+
section flow into 1–3 evenly spaced columns, with nested sections
|
|
318
|
+
spanning the full row. Each ui speaks its own dialect: inline styles for
|
|
319
|
+
`plain` (`<details>`/`<summary>` when collapsible), `Card`/`Accordion` +
|
|
320
|
+
`sx` grids for `mui`, Tailwind classes (`md:grid-cols-2`,
|
|
321
|
+
`bg-card … shadow-sm`, `<details>`) for `shadcn`. The defaults emit
|
|
322
|
+
byte-identical output to 0.4.0.
|
|
323
|
+
|
|
324
|
+
## formstand-cli 0.4.0 — 2026-07-09
|
|
325
|
+
|
|
326
|
+
### Added
|
|
327
|
+
|
|
328
|
+
- `--layout module` works with `--ui mui` and `--ui shadcn`: kit modules
|
|
329
|
+
get a shared `adapter.ts` / `adapter.tsx` exporting the adapter the
|
|
330
|
+
single-file backends inline, and field/section files import it. The
|
|
331
|
+
emitted prop builders are now generic over the field's value type
|
|
332
|
+
(matching the documented example adapters), because the module layout's
|
|
333
|
+
typed hooks surfaced what the single-file layout's widened hooks masked —
|
|
334
|
+
`UseFieldReturn<string>` wasn't assignable to a monomorphic builder.
|
|
335
|
+
Emitted kit modules are typechecked in CI against the MUI stub and both
|
|
336
|
+
the shadcn stub and the repo's real Radix components.
|
|
337
|
+
|
|
338
|
+
### Docs & examples (no package changes)
|
|
339
|
+
|
|
340
|
+
- Brand pass on the docs site and playground: the formstand mark (a form
|
|
341
|
+
with its green check, resting on a music stand) as logo and favicon, a
|
|
342
|
+
brass/ink palette replacing the stock VitePress indigo, hand-drawn SVG
|
|
343
|
+
feature icons replacing the emoji, and restrained motion (hero float,
|
|
344
|
+
card lift, tab/button transitions, demo fade-in) — all disabled under
|
|
345
|
+
prefers-reduced-motion. The playground gains a branded header and
|
|
346
|
+
focus-visible states.
|
|
347
|
+
|
|
348
|
+
## formstand-cli 0.3.0 — 2026-07-09
|
|
349
|
+
|
|
350
|
+
### Added
|
|
351
|
+
|
|
352
|
+
- `--layout module`: emits a feature-module folder instead of one file —
|
|
353
|
+
`schema.ts` (re-exported in zod mode, generated in type mode), `types.ts`,
|
|
354
|
+
`hooks.ts` (`createForm` + `createFormHooks`, so the whole module shares
|
|
355
|
+
one pre-wired hook API), one file per scalar field (props type + field
|
|
356
|
+
hook + component), one file per top-level object/array section (props
|
|
357
|
+
type + section hook built on the path-scoped dirty/valid flags +
|
|
358
|
+
component), the form body, and `index.ts` — the shape of the Onboarding
|
|
359
|
+
playground demo. `--out` names the folder with all destinations
|
|
360
|
+
pre-checked; without it the files stream to stdout with `// --- file:`
|
|
361
|
+
headers. Emitted modules are typechecked in CI against the library
|
|
362
|
+
source, including hostile-name, colliding-name, and leaf-free schemas.
|
|
363
|
+
Requires formstand ≥ 0.7; `--ui plain` only for now.
|
|
364
|
+
- `emitModuleForm` / `joinModuleFiles` join the programmatic API.
|
|
365
|
+
|
|
3
366
|
## 0.7.0 — 2026-07-09
|
|
4
367
|
|
|
5
368
|
### Added
|
package/README.md
CHANGED
|
@@ -429,7 +429,8 @@ form.handleSubmit(async (data) => {
|
|
|
429
429
|
|
|
430
430
|
See [CONTRIBUTING.md](./CONTRIBUTING.md) — note the repo has three
|
|
431
431
|
independent npm roots (`.`, `examples/`, `cli/`), and the root test suite
|
|
432
|
-
needs `examples/` installed.
|
|
432
|
+
needs `examples/` installed. Where the project is headed lives in
|
|
433
|
+
[ROADMAP.md](./ROADMAP.md).
|
|
433
434
|
|
|
434
435
|
## License
|
|
435
436
|
|