[Breaking Changes][lexical] Refactor: Port node classes to the $config() protocol#8640
Draft
etrepum wants to merge 1 commit into
Draft
[Breaking Changes][lexical] Refactor: Port node classes to the $config() protocol#8640etrepum wants to merge 1 commit into
etrepum wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
4a9a8e7 to
9841d04
Compare
9841d04 to
017f878
Compare
017f878 to
7aa5812
Compare
7aa5812 to
d819825
Compare
d819825 to
fb1daf5
Compare
fb1daf5 to
ad30ae4
Compare
ad30ae4 to
2a16d96
Compare
2a16d96 to
e2b9338
Compare
e2b9338 to
7b9e68a
Compare
7b9e68a to
afb0651
Compare
c07d289 to
084cde5
Compare
084cde5 to
20b462a
Compare
## Description Building on the additive $config() protocol changes in the previous commit, this ports the node classes to `$config()` and removes the now-redundant boilerplate static methods (`getType`, `clone`, `importJSON`, `importDOM`) that `getStaticNodeConfig` already synthesizes at runtime, moving each node's `importDOM` map into the `$config` `importDOM` option while leaving `updateFromJSON`/`exportJSON`/`createDOM`/`updateDOM` in place. (It intentionally does not adopt the separate JSON-schema serialization work — serialization methods are unchanged.) Ported across `lexical` (TextNode, TabNode, LineBreakNode, ParagraphNode, RootNode, ArtificialNode), `@lexical/rich-text` (Heading, Quote), `@lexical/link` (Link, AutoLink), `@lexical/mark`, `@lexical/hashtag`, `@lexical/table` (Table, Row, Cell), `@lexical/code` (CodeNode, CodeHighlightNode), `@lexical/react` and `@lexical/extension` HorizontalRule, the playground nodes, and the examples. Decorator nodes whose constructors take required arguments keep a minimal `clone`/`importJSON` (they must construct with arguments). The base `LexicalNode` and the test fixtures that exist to verify the legacy static-method path are left as-is. - Now that a node records its own `type` via the `$config` accessor introduced in the previous commit, the structurally-identical `TabNode` stays nominally distinct from `TextNode` without the statics it previously relied on, so guards like `$isTabNode()` keep narrowing correctly. - Nodes relying on the synthesized `clone` need a zero-argument constructor, so key-only/leading parameters were given explicit defaults (or redundant pure-delegation constructors were removed) so `Node.length === 0`. - Fixed code that relied on `TextNode` being assignable to `TabNode`: `$getFirstCodeNodeOfLine` is now generic over its anchor type. - `__type` is now `readonly` (it is only ever assigned once, in the constructor); `@lexical/yjs`'s generic property writer casts through a mutable record accordingly (it never actually writes `__type`, which is intrinsic and filtered out by its equal-value guard). Tests: - Clone tests use the public `$cloneWithProperties` helper instead of manually pairing `clone()` with `afterCloneFrom()`. - DOM-conversion tests use a new shared `$runDOMConversion` util that drives the editor's registered conversion cache (the paste path) rather than calling a node's static `importDOM()` directly. ## Test plan `pnpm run tsc`, `pnpm run flow`, ESLint, and Prettier all pass. The full unit suite is green: 3728 passed / 1 skipped across `--project unit` and `--project scripts-unit`.
20b462a to
5a5112d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Breaking Changes
Note that these changes only affect callers of static methods of the built-in node classes. It is still completely supported to implement these static methods on your own node classes, but we of course recommend that you upgrade to the
$configprotocol as it's more concise and offers more features.All base classes now use the
$configprotocol which will widen their types static methods, which are technically public API but were really only intended for use directly by the editor (e.g.ParagraphNode.importDOM()orParagraphNode.clone()shouldn't appear in user code).Classes using the
$configprotocol don't directly implement these static methods, their trivial implementations are generated on first call togetStaticNodeConfig. This happens automatically during editor construction, but may happen earlier.It's fine to keep calling these static methods directly, no behavior or type change:
NodeClass.getType()You should avoid calling these static methods directly because they are not intended as callable API surface (other than maybe in tests). Their behavior hasn't changed but the types have widened:
NodeClass.importJSON()- Use higher-level functions to parse serialized nodes such asLexicalEditor.parseEditorStateor$generateNodesFromSerializedNodesNodeClass.importDOM()- You should consider migrating to DOMImportExtension and/or use importDOM indirectly via$generateNodesFromDOMRefactor direct calls, the implementation is present but the behavior has likely changed:
NodeClass.clone()- Use $cloneWithProperties instead. This is present at runtime with a wider type, but only does a trivial construction. The companionafterCloneFrominstance method does all of state propagation.No longer present on refactored classes, do not call:
NodeClass.transform()- This was an@internalAPI, classes that used these have had their transforms moved to the$configdata structure and they are not injected as a static method. It will be deprecated at some point, you can use extensions to register transforms as well as$transformin your$config.Description
The
$config()protocol (added in #8645) lets a node declare its type, cloning, serialization, and DOM-import behavior from a single$config()instance method;getStaticNodeConfigthen synthesizes the corresponding static methods (getType,clone,importJSON,importDOM) at runtime. This PR ports the node classes to$config()and removes the now-redundant boilerplate statics, moving each node'simportDOMmap into the$configimportDOMoption while leavingupdateFromJSON/exportJSON/createDOM/updateDOMin place. It intentionally does not adopt the separate JSON-schema serialization work - serialization is unchanged.Ported across
lexical(TextNode, TabNode, LineBreakNode, ParagraphNode, RootNode, ArtificialNode),@lexical/rich-text(Heading, Quote),@lexical/link(Link, AutoLink),@lexical/mark,@lexical/hashtag,@lexical/table(Table, Row, Cell),@lexical/code(CodeNode, CodeHighlightNode),@lexical/reactand@lexical/extensionHorizontalRule, the playground nodes, and the examples. Decorator nodes whose constructors take required arguments keep a minimalclone/importJSON(they must construct with arguments). The baseLexicalNodeand the fixtures that exist to verify the legacy static-method path are left as-is.$config-based node records its owntypevia the protocol's accessor, the structurally-identicalTabNodestays nominally distinct fromTextNodewithout the statics it previously relied on, so guards like$isTabNode()keep narrowing correctly.cloneneed a zero-argument constructor, so key-only/leading parameters were given explicit defaults (or redundant pure-delegation constructors were removed) soNode.length === 0.TextNodebeing incorrectly assignable toTabNode:$getFirstCodeNodeOfLineis now generic over its anchor type.__typeis nowreadonly(it is only ever assigned once, in the constructor);@lexical/yjs's generic property writer casts through a mutable record accordingly (it never actually writes__type, which is intrinsic and filtered out by its equal-value guard).Tests:
$cloneWithPropertieshelper instead of manually pairingclone()withafterCloneFrom().$runDOMConversionutil that drives the editor's registered conversion cache (the real paste path) instead of a node's staticimportDOM().Test plan
New unit tests