@formkit/validation
Version:
Validation plugin for FormKit
130 lines (94 loc) • 5.46 kB
Markdown
# @formkit/validation
Plugin that runs validation rules against FormKit nodes, manages validation state, and emits error messages to the node store.
## Architecture
```
src/
index.ts - Public API exports
validation.ts - Plugin factory, rule parsing, validation execution
__tests__/
validation.spec.ts - Unit tests for parsing and rule sequencing
```
## Key Abstractions
### Public API
- **`createValidationPlugin(baseRules)`** - Factory returning plugin function. Pass rules from `@formkit/rules` or custom rules.
- **`getValidationMessages(node)`** - Extract all validation messages from node tree as `Map<FormKitNode, FormKitMessage[]>`.
- **`createMessageName(node)`** - Get display name for messages: `validationLabel` > `label` > `name` > `node.name`.
### Types
- **`FormKitValidation`** - Parsed rule with state: `rule`, `args`, `state`, `queued`, `deps`, `observer`, hints.
- **`FormKitValidationIntent`** - Pre-parsed rule tuple: `[ruleName, ...args]`.
- **`FormKitValidationRule`** - Function `(node, ...args) => boolean | Promise<boolean>`. Can have hint properties.
- **`FormKitValidationRules`** - Object mapping rule names to rule functions.
- **`FormKitValidationHints`** - Behavior modifiers: `blocking`, `debounce`, `force`, `skipEmpty`, `name`.
- **`FormKitValidationMessages`** - Object mapping rule names to message strings/functions.
- **`FormKitValidationI18NArgs`** - Arguments passed to i18n message functions: `[{ node, name, args, message? }]`.
### Internal Functions
- **`parseRules(validation, rules, node)`** - Convert string/intent array to `FormKitValidation[]`.
- **`validate(node, validations, state)`** - Entry point for running validation stack.
- **`run(current, validations, state, removeImmediately, complete)`** - Recursive rule executor with dependency tracking.
- **`runRule(validation, node, after)`** - Execute single rule with optional debounce.
- **`createFailedMessage(validation, removeImmediately)`** - Create and observe validation error message.
## Validation Hints (String Syntax)
Rules accept prefix hints in string validation props:
| Hint | Effect | Example |
|------|--------|---------|
| `*` | `force: true` - Run even if prior rule failed | `*contains:foo` |
| `+` | `skipEmpty: false` - Run on empty values | `+required` |
| `?` | `blocking: false` - Don't block form submission | `?email` |
| `(N)` | `debounce: N` - Wait N ms before running | `(200)email` |
Combine hints: `*(200)?email` = forced, debounced 200ms, non-blocking.
## Rule Execution Flow
1. Plugin installed on node via `createValidationPlugin(rules)`
2. Rules parsed from `node.props.validation` (string or array format)
3. Each rule wrapped in `FormKitObservedNode` for dependency tracking
4. Rules run sequentially; failing rule blocks subsequent unless `force: true`
5. Async rules and debounced rules mark node as `validating`
6. Dependencies (cross-node references like `confirm:password`) trigger re-validation when changed
7. Messages stored with key `rule_${ruleName}`
## Message Store Keys
- **`rule_${name}`** - Validation failure message (type: `validation`)
- **`validating`** - State message while async/debounced rules pending (type: `state`, blocking)
- **`failing`** - State tracking if any rule failed (type: `state`, not visible)
## Integration Points
- **Depends on**: `@formkit/core` (node, messages), `@formkit/observer` (dependency tracking), `@formkit/utils` (helpers)
- **Depended on by**: `@formkit/vue` (defaultConfig), `@formkit/i18n` (message types), `@formkit/rules` (rule type)
- **Root CLAUDE.md section**: "validation (@packages/validation)" under Packages > Core
## Modification Guide
### Adding Features
1. New hint types: Extend `FormKitValidationHints`, update `parseHints()` map
2. New message meta: Modify `createFailedMessage()` message creation
3. Execution behavior: Modify `run()` and `validate()` functions
### Adding Custom Rules
Rules are NOT in this package. See `@formkit/rules` for rule implementations. This package only provides:
- The plugin that runs rules
- Parsing validation strings
- Managing validation state
Custom rules via props:
```ts
validationRules: { myRule: (node, ...args) => boolean }
validationMessages: { myRule: ({ name, args }) => string }
```
### Adding Tests
Location: `__tests__/validation.spec.ts`
Run: `pnpm test` from monorepo root
Test patterns used:
- `parseRules()` for parsing verification
- `createValidationPlugin({...})` with mock rules
- `createNode()` with plugin for integration tests
- `await nextTick()` / `setTimeout` for async rule testing
- Check `node.store` for message presence
### Breaking Changes
- Changing `FormKitValidation` structure affects rule observers
- Modifying hint characters breaks existing validation strings
- Changing message keys breaks CSS selectors and i18n
## Auto-Update Triggers
Update this CLAUDE.md when:
- New export added to `src/index.ts`
- New hint character added to `parseHints()`
- New message store key pattern introduced
- `FormKitValidation` type modified
- Public function signature changes
## Deep Dive References
- Rule parsing: `src/validation.ts` lines 540-730 (`parseRules`, `parseHints`, `extractRules`)
- Execution engine: `src/validation.ts` lines 288-400 (`run`, `runRule`)
- Message creation: `src/validation.ts` lines 424-475 (`createFailedMessage`)
- i18n integration: See `@formkit/i18n/src/locales/en.ts` for message format examples