Skip to content

Commit

Permalink
feat(core): support validate options in node and mark attribute def…
Browse files Browse the repository at this point in the history
…initions. (#5991)

ProseMirror allows node and mark specs to define a `validate` function or type, which is used when deserializing JSON, parsing from the DOM, etc.

See https://prosemirror.net/docs/ref/#model.AttributeSpec.validate

Currently, `default` is the only attribute spec option passed through to ProseMirror when resolving the schema. This change updates `getAttributesFromExtensions` and `getSchemaByResolvedExtensions` (and relevant types) to also pass through any defined `validate` attribute option.

To use this, add a `validate` type or function option in any extension's `addAttributes` or `addGlobalAttributes` definition, e.g.:

```
export const NodeId = Extension.create({
  name: 'nodeId',

  addGlobalAttributes() {
    return [
      {
        types: [...],
        attributes: {
          nodeId: {
            default: '__node_id__',
            validate: 'string',
            ...
          }
        }
      }
    ]
  }
});
```

A more complex validation could ensure that the ID is shaped like a UUID, or allow it to be `null` or `undefined`.

Co-authored-by: Nick Perez <[email protected]>
  • Loading branch information
bobthecow and nperez0111 authored Jan 9, 2025
1 parent bfec9b2 commit ff8eed6
Show file tree
Hide file tree
Showing 4 changed files with 11 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-hairs-walk.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@tiptap/core": minor
---

Support `validate` options in node and mark attribute definitions.
3 changes: 2 additions & 1 deletion packages/core/src/helpers/getAttributesFromExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ export function getAttributesFromExtensions(extensions: Extensions): ExtensionAt
const extensionAttributes: ExtensionAttribute[] = []
const { nodeExtensions, markExtensions } = splitExtensions(extensions)
const nodeAndMarkExtensions = [...nodeExtensions, ...markExtensions]
const defaultAttribute: Required<Attribute> = {
const defaultAttribute: Required<Omit<Attribute, 'validate'>> & Pick<Attribute, 'validate'> = {
default: null,
validate: undefined,
rendered: true,
renderHTML: null,
parseHTML: null,
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/helpers/getSchemaByResolvedExtensions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
isolating: callOrReturn(getExtensionField<NodeConfig['isolating']>(extension, 'isolating', context)),
attrs: Object.fromEntries(
extensionAttributes.map(extensionAttribute => {
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }]
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default, validate: extensionAttribute?.attribute?.validate }]
}),
),
})
Expand Down Expand Up @@ -132,7 +132,7 @@ export function getSchemaByResolvedExtensions(extensions: Extensions, editor?: E
code: callOrReturn(getExtensionField<MarkConfig['code']>(extension, 'code', context)),
attrs: Object.fromEntries(
extensionAttributes.map(extensionAttribute => {
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default }]
return [extensionAttribute.name, { default: extensionAttribute?.attribute?.default, validate: extensionAttribute?.attribute?.validate }]
}),
),
})
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export type KeyboardShortcutCommand = (props: { editor: Editor }) => boolean

export type Attribute = {
default?: any
validate?: string | ((value: any) => void)
rendered?: boolean
renderHTML?: ((attributes: Record<string, any>) => Record<string, any> | null) | null
parseHTML?: ((element: HTMLElement) => any | null) | null
Expand All @@ -239,7 +240,7 @@ export type Attributes = {
export type ExtensionAttribute = {
type: string
name: string
attribute: Required<Attribute>
attribute: Required<Omit<Attribute, 'validate'>> & Pick<Attribute, 'validate'>
}

export type GlobalAttributes = {
Expand Down

0 comments on commit ff8eed6

Please sign in to comment.