Skip to content

v0.53.0

Latest

Choose a tag to compare

@zurfyx zurfyx released this 28 Oct 15:52
· 9 commits to main since this release

v0.53.0 is the monthly release and collects a month of work since v0.52.0. It removes the extensions deprecated as of v0.52.0 and the unchecked node type arguments, starts a curated CHANGELOG.md with migration notes, and adds several opt-in, experimental building blocks for structured documents and collaboration: declarative node structure in $config, stable block ids, set-valued NodeState with a wrapper-free MarkStateExtension, and GFM tables in @lexical/mdast. Fixes cover IME composition, spellcheck, inline decorators, iOS taps and drops, tables, Markdown, and the ordering of commit listeners that collaboration depends on.

Breaking Changes

  • All packages: extensions deprecated as of v0.52.0 are removed (#9316). Migrate as follows:
    • CodeImportExtension (@lexical/code-core): use CodeExtension
    • CodeHighlighterShikiExtension, CodeHighlighterShikiConfig (@lexical/code-shiki): use CodeShikiExtension with {tokenizer}
    • HorizontalRuleImportExtension (@lexical/html): use HorizontalRuleExtension plus any extension that brings in CoreImportExtension
    • LinkImportExtension, ListImportExtension, RichTextImportExtension, TableImportExtension: use LinkExtension, ListExtension, RichTextExtension, TableExtension
    • MdastImportExtension, MdastExportExtension and their *Output types (@lexical/mdast): use MdastExtension and MdastExtensionOutput
    • The *ImportRules arrays are not extensions and remain.
  • @lexical/mdast (experimental): table column alignment is no longer stored in the mdastTableAlign NodeState. Alignment is now the element format of the paragraph in each cell, so editor states serialized with the old state lose their column alignment on load (#9478)
  • lexical: The deprecated unchecked type arguments on node traversal and key lookup are removed from TypeScript and Flow: getParent<T>(), getParentOrThrow<T>(), getChildren<T>(), getFirstChild<T>(), getNextSibling<T>(), $getNodeByKey<T>() and the rest of that family. Runtime behavior is unchanged. Remove the type argument and narrow the result with a node guard such as $isListItemNode (#9479)
  • lexical: ElementNode.reconcileObservedMutation (@internal) restores the DOM of the element's committed children from the latest reconciled state, not the children of the update it is called in. Code that called it inside an update to render that update's children must place those children's DOM itself (#9329)

New APIs & Features

  • lexical / @lexical/extension (experimental): Declarative node structure in $config. New optional keys inline, children, mergeAdjacent, and a derived flag on fields and states describe what an element may hold and when adjacent siblings are the same thing, so importers, serializers and storage layers can read rules that used to live only in transforms. getDeclaredNodeStructure(klass) reads them, and the opt-in NodeStructureExtension enforces them (#9452)
  • @lexical/extension / @lexical/html (experimental): Stable block ids. BlockIdExtension gives blocks an id that survives serialization, reloads, undo/redo, the clipboard and collaboration, stored as NodeState ("$": {"blockId": "…"}), so agents and server pipelines can address blocks directly. BlockIdHTMLExtension, imported from @lexical/html/BlockIdHTMLExtension, carries the ids through HTML (#9421, #9474)
  • lexical / @lexical/mark (experimental): Set-valued NodeState and MarkStateExtension. arraySetValue(item) declares a state whose value is a canonical (sorted, deduplicated, frozen) set of strings. MarkStateExtension uses it to store mark ids on the leaves they cover instead of in MarkNode wrappers, so overlapping comments need no tree restructuring and ids travel with the text (#9380)
  • @lexical/mdast (experimental): GFM tables. MdastTableExtension imports tables the way GFM renders them and exports any table as a GFM table, using <br> for multi-block cells and padding merged or ragged cells (#9478)
  • @lexical/clipboard: Clipboard import middleware can replace the data and selection for the remaining handlers with $next(data?, selection?), the equivalent of ProseMirror's transformPastedHTML, so off-the-shelf HTML cleanup can be plugged in (#9312)
  • @lexical/link: ClickableLinkExtension accepts getWindow(editor), so iframe editors can navigate in their own window or leave the click unhandled (#9360)
  • @lexical/react: LexicalAutoEmbedPlugin accepts $disableMenuFor to suppress the embed menu where a block embed does not belong, such as inline-only slots (#9365)
  • Changelog with migration notes: CHANGELOG.md is now built from changesets, with Breaking changes (each with Migration: and Detect:), Behavior changes, Deprecations and the full commit list for every release, and it ships in the lexical package at node_modules/lexical/CHANGELOG.md for people and tools upgrading across versions (#9357)
  • Continuous builds: every PR and main commit is published to pkg.pr.new, so a fix can be tried before the next release with npm i https://pkg.pr.new/lexical@main (npm only; see the PR for pnpm and yarn) (#9385, #9387, #9394)

Notable Fixes

Security

  • Links opened in a new tab by registerClickableLink / ClickableLinkPlugin no longer get window.opener, closing a reverse-tabnabbing path (#9303)
  • Markdown export escapes literal brackets and significant < and &, so literal text no longer turns into HTML, character references or links when the Markdown is parsed again. Import decodes escapes and character references in a single pass (#9481)

Input, composition & spellcheck

  • Composing into a TextNode subclass with an IME or a dead key no longer replaces it with a plain TextNode, so it keeps its type, key and NodeState (#9291); cancelled macOS Pinyin compositions in desktop WebKit finish, so the next Enter works (#9362)
  • Chromium keeps spellcheck underlines when Lexical rewrites a text node (#9354); Markdown shortcuts work in iframe editors (#9352); selectionchange events from unfocused inputs and textareas no longer clear pending formatting (#9477); copy, cut and paste inside a decorator's own input are left to that input (#9288)

Selection & editing

  • Mouse drags select across inline decorators such as images, equations and mentions (#9329); line deletion keeps the line break before a line starting with an inline decorator and preserves decorators across soft wraps (#9301, #9321)
  • Selected text and formatting are preserved at range boundaries, and $patchStyleText now styles the selection it is given (#9261); caret mutations, endpoint repair and cross-parent moves no longer corrupt sibling links, throw, or misorder the DOM (#9262, #9263, #9302)
  • Selections and deletions stay inside slot boundaries (#9373); root text content stays correct after repeated slot edits and for custom getTextContent() overrides (#9283, #9260)
  • Enter splits a heading correctly after a line break or inline decorator (#9295); removing a list keeps a caret that sits after an inline decorator (#9300); HISTORY_PUSH_TAG always pushes its own undo entry (#9218); setting a NodeState key still held in unknownState no longer leaves the stale JSON value behind (#9468)

iOS

  • Tapping a decorator no longer scrolls the page to the top of the editor (#9325); double-tapping an empty table cell stays in the cell (#9267); native text-file drops insert their text (#9320)

Collaboration

  • Commit listeners stay in order when editor.read() is called during listener dispatch, fixing lost content with CollabV2 and the Expected shared type to include type attribute corruption in CollabV1 (#9335, closes #7709)
  • @lexical/yjs re-creates only the children that actually moved, keeping sibling Yjs identities and turning a 1.3 s move in an 8,000-paragraph document into a fast one (#9349)
  • Table commands repair ragged tables, which concurrent row and column inserts can produce, instead of throwing (#9375)

Tables, links, code & Markdown

  • Clicks on a nested editor's table, and on nested links, are left to the nested editor (#9356, #9360); $toggleLink(null) no longer throws when the selection only touches a link edge (#9412)
  • Imported HTML code content is normalized to code lines (#9272); Tab works in a code block whose language the highlighter does not know (#9472)
  • Markdown import handles out-of-range character references and keeps backslashes in leading code spans (#9388, #9396); @lexical/mdast keeps consecutive and leading line breaks in a round trip (#9392)

React, SSR & misc

  • Composers no longer read Math.random() while rendering on the server, so Next.js 16 prerendering with cacheComponents works without Suspense (#9319)
  • A read-only ContentEditable no longer renders an unnamed role="textbox" (#9270); stale asynchronous auto-embed and typeahead results are ignored (#9331, #9332); positionNodeOnRange no longer loops forever once its root is detached (#9358)
  • The "node does not exist in active editor state" error names the node and explains how to guard a stale reference (#9366); garbage collection no longer revisits dirty subtrees (#9264)
  • DevTools: the Edge and Safari store listings are gone, and Chrome publishing moves to Web Store API v2 and runs automatically after each latest npm release (#9296, #9457, #9369)

What's Changed

New Contributors

Full Changelog: https://github.com/facebook/lexical/compare/v0.52.0...v0.53.0