Skip to content

Proposal: Lexical Schemas #3833

@tylerjbainbridge

Description

@tylerjbainbridge

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) 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.

class TableRowNode extends DEPRECATED_GridRowNode {
  static getSchema(): Schema<TableRowNode> {
    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
        }
    }
})

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions