facebook / facebook/lexical

Proposal: Lexical Schemas

Open
#3,833 41 comments 34 reactions 2 assignees Claimed by @acywatson View on GitHub
discussion enhancement feature
Dominant language
TypeScript
Stars
23.9k
Forks
2.2k
Avg merge
1d 14h
Merged PRs (30d)
55

Description

# Lexical Schemas

### The Problem

A lot of mistakes with Lexical are caused by developers adding nodes where they shouldn’t.

Schemas are an API proposal that’s designed to add guardrails around the insertion of nodes. They'll check if a given relationship (parent, child, sibling) is valid and allow you to normalize it if possible.

Slate.js solves this elegantly with their [Schema](https://docs.slatejs.org/v/v0.47/guides/schemas) (https://docs.slatejs.org/v/v0.47/guides/schemas) API and developers find it intuitive, so it makes a lot of sense to propose a similar API that’s familiar to users of Slate and other Rich Text solutions.

### The Solution

We’ll stick with our current approach of encapsulating node-related logic on the class itself by introducing a new static method, getSchema, which returns a Lexical Schema.

The schema’s relation methods will be called at different points within the core insertion logic and aim to remove the many çase-specific patches and node methods throughout the codebase while catching bugs before hitting production.

If any of the validations fail it will warn the developer in the console and we’ll provide a normalize method that’ll act as an escape hatch and let you to fix the issue.

### Example

*Take the following Table Row example*
A row should never not be a child of a table, it should never have a child that isn’t a cell, and it should never be a sibling that isn’t another row. This is expressed almost verbatim in code below.

```js
class TableRowNode extends DEPRECATED_GridRowNode {
static getSchema(): Schema {
return {
relations: {
parent(a: TableRowNode, parent: LexicalNode): boolean {
return $isTableNode(parent);
},
child(a: TableRowNode, child: LexicalNode): boolean {
return $isTableCellNode(child);
},
sibling(a: TableRowNode, sibling: LexicalNode): boolean {
return $isTableRowNode(sibling);
},
},
normalize(node: TableRowNode, error: LexicalSchemaError, editor: LexicalEditor) {
if (error.type === 'invalid_parent') {
// try to repair
}
}
}
}
}

// For Product specific validations an API like this could also be useful.
$extendSchema(TableRowNode, {
relations: {
parent(a: TableRowNode, parent: LexicalNode): boolean {
// something
},
child(a: TableRowNode, child: LexicalNode): boolean {
// something
},
sibling(a: TableRowNode, sibling: LexicalNode): boolean {
// something
},
},
normalize(node: TableRowNode, error: LexicalSchemaError, editor: LexicalEditor) {
if (error.type === 'invalid_parent') {
// try to repair
}
}
})
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.