Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 28 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ This is opt-in and local to the component author:

- it does not change `defineComponent(...)`
- it validates only the keys you list
- it throws immediately on the first invalid prop
- it follows `config.propValidationMode`
- it does not coerce values
- it does not mutate `head.props`

Expand Down Expand Up @@ -156,6 +156,7 @@ pval.oneOf(['create', 'edit'] as const)
pval.arrayOf(pval.isString)
pval.shape({ title: pval.isString, count: pval.isNumber })
pval.refOf(pval.isString)
pval.fail('title', 'expected non-empty string')
```

### Dynamic bindings and refs
Expand Down Expand Up @@ -199,11 +200,11 @@ head.validateProps({
Users can provide their own validators as long as they match the `PropValidator<T>` signature:

```ts
import { type PropValidator } from 'regor'
import { pval, type PropValidator } from 'regor'

const isNonEmptyString: PropValidator<string> = (value, name) => {
if (typeof value !== 'string' || value.trim() === '') {
throw new Error(`Invalid prop "${name}": expected non-empty string.`)
pval.fail(name, 'expected non-empty string')
}
}

Expand All @@ -218,11 +219,34 @@ Custom validators can also use the third `head` argument:
const startsWithPrefix: PropValidator<string> = (value, name, head) => {
const ctx = head.requireContext(AppServices)
if (typeof value !== 'string' || !value.startsWith(ctx.prefix)) {
throw new Error(`Invalid prop "${name}": expected prefixed value.`)
pval.fail(name, 'expected prefixed value')
}
}
```

### Validation mode

Validation behavior is controlled through `RegorConfig.propValidationMode`:

```ts
import { RegorConfig } from 'regor'

const config = new RegorConfig()
config.propValidationMode = 'warn'
```

Available modes:

- `'throw'` (default): throw immediately on invalid prop
- `'warn'`: report through `warningHandler.warning(...)` and continue
- `'off'`: skip runtime prop validation entirely

Pass the config into `createApp(...)` when you want app-level control:

```ts
createApp(appContext, template, config)
```

## Table Templates and Components

Regor preprocesses table-related templates to keep markup valid when using
Expand Down
15 changes: 15 additions & 0 deletions docs-site/src/content/docs/api/createApp.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ To create a Regor application, call the `createApp` function with the following
- `isSVG` (boolean, optional): Indicates whether the template contains SVG elements.

- `config` (optional): An optional configuration object for customizing Regor's behavior.
- `config.propValidationMode`: Controls whether component prop validation throws, warns, or is disabled.

### Example

Expand All @@ -45,6 +46,17 @@ const app = createApp(appContext) // default template: { selector: '#app'}

- `config` (optional): An optional configuration object that customizes Regor's behavior. It allows you to specify various options.

Example:

```ts
import { createApp, RegorConfig } from 'regor'

const config = new RegorConfig()
config.propValidationMode = 'warn'

createApp(appContext, template, config)
```

## Return Value

The `createApp` function returns an object with the following properties:
Expand All @@ -68,12 +80,15 @@ The `createApp` function returns an object with the following properties:
`tbody`, and `tfoot`.

- The `config` parameter lets you customize Regor's behavior to suit your application's requirements.
- `config.propValidationMode` controls how `head.validateProps(...)` behaves for components created under that app.

- You can use the `unmount` and `unbind` functions to clean up and remove the app from the DOM when it's no longer needed.

## See Also

- [`defineComponent`](/api/defineComponent)
- [`RegorConfig`](/api/regorConfig)
- [`pval`](/api/pval)
- [`toFragment`](/api/toFragment)
- [`toJsonTemplate`](/api/toJsonTemplate)

Expand Down
12 changes: 11 additions & 1 deletion docs-site/src/content/docs/api/defineComponent.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ const Card = defineComponent<Card>(html`<article>{{ summary }}</article>`, {
Behavior:

- validates only the listed keys
- throws on first invalid prop
- follows `config.propValidationMode`
- does not mutate `head.props`
- does not coerce values

Expand Down Expand Up @@ -122,6 +122,16 @@ const Card = defineComponent<Card>(html`<article>{{ summary }}</article>`, {

For object-style `:context="{ ... }"` values, validate the object shape directly with `pval.shape(...)`.

### Validation mode

`head.validateProps(...)` follows the active `RegorConfig.propValidationMode`:

- `'throw'` (default): throw on invalid prop
- `'warn'`: warn and continue
- `'off'`: disable runtime validation

Set it on the config passed to `createApp(...)`.

## Return Value

The `defineComponent` function returns a component object with the following properties:
Expand Down
41 changes: 37 additions & 4 deletions docs-site/src/content/docs/api/pval.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,24 @@ Validation is:
2. local to the component
3. runtime-only
4. non-coercive
5. fail-fast
5. controlled by `RegorConfig.propValidationMode`

## Validation mode

Runtime prop-validation behavior is controlled through `RegorConfig.propValidationMode`:

```ts
import { RegorConfig } from 'regor'

const config = new RegorConfig()
config.propValidationMode = 'warn'
```

Modes:

1. `'throw'` (default): invalid props throw immediately.
2. `'warn'`: invalid props are reported through `warningHandler.warning(...)`.
3. `'off'`: runtime prop validation is skipped.

## Built-in validators

Expand Down Expand Up @@ -153,6 +170,22 @@ head.validateProps({
})
```

### `pval.fail(name, detail)`

Throws the same structured validation failure used by Regor's built-in validators.

This is the recommended way for custom validators to report invalid input,
because it preserves nested prop paths and lets `head.validateProps(...)`
format the final component-aware error consistently.

```ts
const isNonEmptyString: PropValidator<string> = (value, name) => {
if (typeof value !== 'string' || value.trim() === '') {
pval.fail(name, 'expected non-empty string')
}
}
```

## Dynamic props vs object props

Single-prop bindings like `:title="titleRef"` may arrive as refs at runtime, so `pval.refOf(...)` is appropriate there.
Expand All @@ -164,11 +197,11 @@ Object-style `:context="{ meta: { slug: 'x' } }"` values can be validated direct
You are not limited to `pval`. Users can write any validator that matches `PropValidator<T>`:

```ts
import { type PropValidator } from 'regor'
import { pval, type PropValidator } from 'regor'

const isNonEmptyString: PropValidator<string> = (value, name) => {
if (typeof value !== 'string' || value.trim() === '') {
throw new Error(`Invalid prop "${name}": expected non-empty string.`)
pval.fail(name, 'expected non-empty string')
}
}
```
Expand All @@ -179,7 +212,7 @@ Custom validators also receive `head` as the third argument:
const startsWithPrefix: PropValidator<string> = (value, name, head) => {
const services = head.requireContext(AppServices)
if (typeof value !== 'string' || !value.startsWith(services.prefix)) {
throw new Error(`Invalid prop "${name}": expected prefixed value.`)
pval.fail(name, 'expected prefixed value')
}
}
```
Expand Down
23 changes: 23 additions & 0 deletions docs-site/src/content/docs/api/regorConfig.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,27 @@ This property holds the global context object, which can be used to define globa
This property controls whether Regor should enable interpolation for data binding. When set to `true`, Regor will automatically perform data binding using the `{{ }}` syntax in templates.
Interpolation also supports the `[[ ]]` syntax.

### `propValidationMode` (`'throw' | 'warn' | 'off'`)

- Default: `'throw'`

Controls how `head.validateProps(...)` behaves when a validator fails.

- `'throw'`: throw immediately on invalid prop
- `'warn'`: forward the validation failure to `warningHandler.warning(...)` and continue
- `'off'`: disable runtime prop validation

Example:

```ts
import { RegorConfig, createApp } from 'regor'

const config = new RegorConfig()
config.propValidationMode = 'warn'

createApp(appContext, template, config)
```

## Methods

### `addComponent(...)`
Expand Down Expand Up @@ -122,6 +143,7 @@ The `RegorConfig` class provides default configuration values for various proper

- The `useInterpolation` property enables or disables the use of `{{}}` syntax for data binding in templates.
- Interpolation supports both `{{ }}` and `[[ ]]` syntaxes.
- The `propValidationMode` property controls whether component prop validation throws, warns, or is disabled.

- Registering custom components using `addComponent` makes those components available for use in your Regor templates.

Expand All @@ -133,6 +155,7 @@ The `RegorConfig` class provides default configuration values for various proper

- [`createApp`](/api/createApp)
- [`defineComponent`](/api/defineComponent)
- [`pval`](/api/pval)
- [`toFragment`](/api/toFragment)
- [`toJsonTemplate`](/api/toJsonTemplate)

Expand Down
32 changes: 28 additions & 4 deletions docs-site/src/content/docs/guide/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ This validation model is intentionally runtime-first:

1. It is fully opt-in.
2. It validates only the keys you list.
3. It throws immediately on the first invalid prop.
3. It follows the active `propValidationMode` config.
4. It does not coerce values.
5. It does not mutate `head.props`.

Expand Down Expand Up @@ -163,6 +163,7 @@ const EditorCard = defineComponent<EditorCard>(
8. `pval.arrayOf(validator)`
9. `pval.shape({ ... })`
10. `pval.refOf(validator)`
11. `pval.fail(name, detail)`

Example:

Expand Down Expand Up @@ -225,11 +226,11 @@ head.validateProps({
Users are not limited to the built-in validators. Any function matching `PropValidator<T>` can be used:

```ts
import { type PropValidator } from 'regor'
import { pval, type PropValidator } from 'regor'

const isNonEmptyString: PropValidator<string> = (value, name) => {
if (typeof value !== 'string' || value.trim() === '') {
throw new Error(`Invalid prop "${name}": expected non-empty string.`)
pval.fail(name, 'expected non-empty string')
}
}

Expand All @@ -244,7 +245,7 @@ Custom validators can also use the third `head` argument:
const startsWithParentPrefix: PropValidator<string> = (value, name, head) => {
const ctx = head.requireContext(AppServices)
if (typeof value !== 'string' || !value.startsWith(ctx.prefix)) {
throw new Error(`Invalid prop "${name}": expected prefixed value.`)
pval.fail(name, 'expected prefixed value')
}
}
```
Expand All @@ -258,6 +259,29 @@ Validation is most useful when:
3. You want an explicit runtime guard before local state mapping.
4. You set `head.autoProps = false` and map incoming props manually.

### Validation mode

Validation behavior is configured through `RegorConfig.propValidationMode`:

```ts
import { RegorConfig } from 'regor'

const config = new RegorConfig()
config.propValidationMode = 'warn'
```

Modes:

1. `'throw'` (default): throws on the first invalid prop.
2. `'warn'`: sends the validation failure to `warningHandler.warning(...)` and continues.
3. `'off'`: skips runtime prop validation.

Pass the config into `createApp(...)` to apply the mode for that app:

```ts
createApp(appContext, template, config)
```

## How Component Inputs Are Routed

On a component host tag, Regor routes bindings through two different channels:
Expand Down
4 changes: 2 additions & 2 deletions docs-site/src/content/docs/guide/typescript.md
Original file line number Diff line number Diff line change
Expand Up @@ -162,11 +162,11 @@ const TitleCard = defineComponent<TitleCard>(
You can also write custom validators:

```ts
import { type PropValidator } from 'regor'
import { pval, type PropValidator } from 'regor'

const isNonEmptyString: PropValidator<string> = (value, name) => {
if (typeof value !== 'string' || value.trim() === '') {
throw new Error(`Invalid prop "${name}": expected non-empty string.`)
pval.fail(name, 'expected non-empty string')
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "regor",
"version": "1.4.6",
"version": "1.4.7",
"description": "A modern UI framework for web and desktop applications, inspired by Vue's concepts and powered by simplicity and flexibility.",
"author": "Ahmed Yasin Koculu",
"license": "MIT",
Expand Down
Loading
Loading