Defining a Dialect
What a dialect declares, and how it takes shape in the meta-schema.
A dialect is a concrete vocabulary of the language, defined by one artifact: its meta-schema, the JSON Schema that validates the dialect's documents. Publishing the meta-schema is what publishes the dialect.
Defining a dialect means deciding three things and expressing them in the meta-schema: an identity, the element types, and each type's props. How the meta-schema is produced, by hand or by tooling, is out of scope; only the result is.
Identity
A dialect is identified by a URI. The URI is the meta-schema's $id, and
a document declares its dialect by naming the URI in $schema. Like
documents,
dialects are versioned by URI: a revised dialect is published under a new
identifier.
Element Types
Each element type appears in the meta-schema as one conditional in the
elements definition, carrying
the type's prop schemas and naming its required props; an unknown type
matches no conditional and is rejected through unevaluatedProperties.
Prop Schemas
A prop schema states exactly what the prop accepts. A static prop states its value schema directly; in a document, the prop then holds a literal matching that schema, or an expression resolving to one. The language's dynamic prop kinds are declared by referencing the definitions:
| To declare | Prop schema |
|---|---|
| A slot accepting nested elements | { "$ref": "#/$defs/children" } |
| A callback dispatching actions | { "$ref": "#/$defs/actions" } |
| A bindable field | { "anyOf": [{ "$ref": "#/$defs/field" }, <value schema>] } |
A bindable field accepts a $field binding or a literal matching its
value schema; drop the value alternative to require a binding.
In the meta-schema, every prop schema is wrapped into its prop position, which adds what all props accept uniformly: expressions in every value position, and the conditional and map wrappers. A prop schema never repeats those.
A prop schema is embedded into the meta-schema verbatim, so a $ref
inside it resolves against the meta-schema's own definitions, not the
dialect's.
Example
The prop schemas of an input element with a label, a bindable string
value, and a change callback:
{
"label": { "type": "string" },
"value": {
"anyOf": [
{ "$ref": "#/$defs/field" },
{ "type": "string" }
]
},
"onChange": { "$ref": "#/$defs/actions" }
}With label required, the type's conditional in the meta-schema's
elements definition becomes:
{
"if": {
"properties": { "type": { "const": "input" } },
"required": ["type"]
},
"then": {
"properties": {
"type": { "const": "input" },
"props": {
"type": "object",
"properties": {
"label": { /* Insert prop position here */ },
"value": { /* Insert prop position here */ },
"onChange": { /* Insert prop position here */ }
},
"required": ["label"],
"additionalProperties": false
}
},
"required": ["type", "props"]
}
}Each commented slot holds the declared prop schema wrapped into its prop position; Prop Positions specifies the exact shape.