Skip to content

Commit dd4a77b

Browse files
authored
Avoid overly-cautious type guards in favor of more performant type discriminators (#5982)
* add `isEditorNode`, `isElementNode`, and `isTextNode` and switch to using them whenever possible. also switch to `node === editor` checks where it makes sense also add a comment to a confusing failure state of `Node.parent` also remove a superfluous call to `Editor.nodes` in certain codepaths of `Editor.wrapNodes` * fix deep checking for `isEditor` and check for new properties from #5307 * convert all instances of `Element.isAncestor` and a few I missed before * changeset * fix pretter complaint in changeset * use new guards in other packages and examples * revert wrap nodes change (keeping new guards) * add other packages to changeset * preemptively update peerDependencies * renamed isXNode functions to Node.isX * replaced a few places where isText is passed as a value directly * removed unneeded imports * update yarn lock so tests can pass * added `Node.isAncestor` sugar function * update jsx examples
1 parent 44bfa34 commit dd4a77b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+371
-427
lines changed

.changeset/rich-cheetahs-battle.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
'slate': minor
3+
'slate-react': patch
4+
'slate-dom': patch
5+
---
6+
7+
Add `Node.isEditor`, `Node.isElement`, and `Node.isText` as alternative type guards for when we already know the object is a node.
8+
Use these new functions instead of `Editor.isEditor`, `Element.isElement`, and `Text.isText` whenever possible, the classic functions are only necessary for typechecking an entirely unknown object.
9+
===

packages/slate-dom/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
"source-map-loader": "^4.0.1"
3535
},
3636
"peerDependencies": {
37-
"slate": ">=0.99.0"
37+
"slate": ">=0.121.0"
3838
},
3939
"umdGlobals": {
4040
"slate": "Slate"

packages/slate-dom/src/plugin/dom-editor.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
BaseEditor,
33
Editor,
4-
Element,
54
Node,
65
Path,
76
Point,
@@ -329,7 +328,7 @@ export const DOMEditor: DOMEditorInterface = {
329328
// If the drop target is inside a void node, move it into either the
330329
// next or previous node, depending on which side the `x` and `y`
331330
// coordinates are closest to.
332-
if (Element.isElement(node) && Editor.isVoid(editor, node)) {
331+
if (Node.isElement(node) && Editor.isVoid(editor, node)) {
333332
const rect = target.getBoundingClientRect()
334333
const isPrev = editor.isInline(node)
335334
? x - rect.left < rect.left + rect.width - x
@@ -396,7 +395,7 @@ export const DOMEditor: DOMEditorInterface = {
396395
const parent = NODE_TO_PARENT.get(child)
397396

398397
if (parent == null) {
399-
if (Editor.isEditor(child)) {
398+
if (child === editor) {
400399
return path
401400
} else {
402401
break
@@ -548,21 +547,22 @@ export const DOMEditor: DOMEditorInterface = {
548547

549548
isTargetInsideNonReadonlyVoid: (editor, target) => {
550549
if (IS_READ_ONLY.get(editor)) return false
550+
if (!DOMEditor.hasTarget(editor, target)) return false
551551

552-
const slateNode =
553-
DOMEditor.hasTarget(editor, target) &&
554-
DOMEditor.toSlateNode(editor, target)
555-
return Element.isElement(slateNode) && Editor.isVoid(editor, slateNode)
552+
const slateNode = DOMEditor.toSlateNode(editor, target)
553+
return Node.isElement(slateNode) && Editor.isVoid(editor, slateNode)
556554
},
557555

558556
setFragmentData: (editor, data, originEvent) =>
559557
editor.setFragmentData(data, originEvent),
560558

561559
toDOMNode: (editor, node) => {
562-
const KEY_TO_ELEMENT = EDITOR_TO_KEY_TO_ELEMENT.get(editor)
563-
const domNode = Editor.isEditor(node)
564-
? EDITOR_TO_ELEMENT.get(editor)
565-
: KEY_TO_ELEMENT?.get(DOMEditor.findKey(editor, node))
560+
const domNode =
561+
node === editor
562+
? EDITOR_TO_ELEMENT.get(editor)
563+
: EDITOR_TO_KEY_TO_ELEMENT.get(editor)?.get(
564+
DOMEditor.findKey(editor, node)
565+
)
566566

567567
if (!domNode) {
568568
throw new Error(

packages/slate-dom/src/plugin/with-dom.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
BaseEditor,
33
Editor,
4-
Element,
54
Node,
65
Operation,
76
Path,
@@ -97,7 +96,7 @@ export const withDOM = <T extends BaseEditor>(
9796

9897
if (e.selection && Range.isCollapsed(e.selection)) {
9998
const parentBlockEntry = Editor.above(e, {
100-
match: n => Element.isElement(n) && Editor.isBlock(e, n),
99+
match: n => Node.isElement(n) && Editor.isBlock(e, n),
101100
at: e.selection,
102101
})
103102

packages/slate-dom/src/utils/diff-text.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,4 @@
1-
import {
2-
Editor,
3-
Node,
4-
Operation,
5-
Path,
6-
Point,
7-
Range,
8-
Text,
9-
Element,
10-
} from 'slate'
1+
import { Editor, Node, Operation, Path, Point, Range } from 'slate'
112
import { EDITOR_TO_PENDING_DIFFS } from './weak-maps'
123

134
export type StringDiff = {
@@ -33,7 +24,7 @@ export function verifyDiffState(editor: Editor, textDiff: TextDiff): boolean {
3324
}
3425

3526
const node = Node.get(editor, path)
36-
if (!Text.isText(node)) {
27+
if (!Node.isText(node)) {
3728
return false
3829
}
3930

@@ -49,7 +40,7 @@ export function verifyDiffState(editor: Editor, textDiff: TextDiff): boolean {
4940
}
5041

5142
const nextNode = Node.get(editor, nextPath)
52-
return Text.isText(nextNode) && nextNode.text.startsWith(diff.text)
43+
return Node.isText(nextNode) && nextNode.text.startsWith(diff.text)
5344
}
5445

5546
export function applyStringDiff(text: string, ...diffs: StringDiff[]) {
@@ -170,12 +161,12 @@ export function normalizePoint(editor: Editor, point: Point): Point | null {
170161
}
171162

172163
let leaf = Node.get(editor, path)
173-
if (!Text.isText(leaf)) {
164+
if (!Node.isText(leaf)) {
174165
return null
175166
}
176167

177168
const parentBlock = Editor.above(editor, {
178-
match: n => Element.isElement(n) && Editor.isBlock(editor, n),
169+
match: n => Node.isElement(n) && Editor.isBlock(editor, n),
179170
at: path,
180171
})
181172

@@ -184,7 +175,7 @@ export function normalizePoint(editor: Editor, point: Point): Point | null {
184175
}
185176

186177
while (offset > leaf.text.length) {
187-
const entry = Editor.next(editor, { at: path, match: Text.isText })
178+
const entry = Editor.next(editor, { at: path, match: Node.isText })
188179
if (!entry || !Path.isDescendant(entry[1], parentBlock[1])) {
189180
return null
190181
}

packages/slate-react/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@
4242
"peerDependencies": {
4343
"react": ">=18.2.0",
4444
"react-dom": ">=18.2.0",
45-
"slate": ">=0.114.0",
46-
"slate-dom": ">=0.119.0"
45+
"slate": ">=0.121.0",
46+
"slate-dom": ">=0.119.1"
4747
},
4848
"umdGlobals": {
4949
"react": "React",

packages/slate-react/src/components/editable.tsx

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ export const Editable = forwardRef(
669669
) {
670670
const block = Editor.above(editor, {
671671
at: anchor.path,
672-
match: n => Element.isElement(n) && Editor.isBlock(editor, n),
672+
match: n => Node.isElement(n) && Editor.isBlock(editor, n),
673673
})
674674

675675
if (block && Node.string(block[0]).includes('\t')) {
@@ -1186,7 +1186,7 @@ export const Editable = forwardRef(
11861186
relatedTarget
11871187
)
11881188

1189-
if (Element.isElement(node) && !editor.isVoid(node)) {
1189+
if (Node.isElement(node) && !editor.isVoid(node)) {
11901190
return
11911191
}
11921192
}
@@ -1234,13 +1234,12 @@ export const Editable = forwardRef(
12341234
let blockPath = path
12351235
if (
12361236
!(
1237-
Element.isElement(node) &&
1238-
Editor.isBlock(editor, node)
1237+
Node.isElement(node) && Editor.isBlock(editor, node)
12391238
)
12401239
) {
12411240
const block = Editor.above(editor, {
12421241
match: n =>
1243-
Element.isElement(n) && Editor.isBlock(editor, n),
1242+
Node.isElement(n) && Editor.isBlock(editor, n),
12441243
at: path,
12451244
})
12461245

@@ -1435,10 +1434,7 @@ export const Editable = forwardRef(
14351434
// default, and calling `preventDefault` hides the cursor.
14361435
const node = ReactEditor.toSlateNode(editor, event.target)
14371436

1438-
if (
1439-
Element.isElement(node) &&
1440-
Editor.isVoid(editor, node)
1441-
) {
1437+
if (Node.isElement(node) && Editor.isVoid(editor, node)) {
14421438
event.preventDefault()
14431439
}
14441440
}
@@ -1455,8 +1451,7 @@ export const Editable = forwardRef(
14551451
const node = ReactEditor.toSlateNode(editor, event.target)
14561452
const path = ReactEditor.findPath(editor, node)
14571453
const voidMatch =
1458-
(Element.isElement(node) &&
1459-
Editor.isVoid(editor, node)) ||
1454+
(Node.isElement(node) && Editor.isVoid(editor, node)) ||
14601455
Editor.void(editor, { at: path, voids: true })
14611456

14621457
// If starting a drag on a void node, make sure it is selected
@@ -1836,7 +1831,7 @@ export const Editable = forwardRef(
18361831
)
18371832

18381833
if (
1839-
Element.isElement(currentNode) &&
1834+
Node.isElement(currentNode) &&
18401835
Editor.isVoid(editor, currentNode) &&
18411836
(Editor.isInline(editor, currentNode) ||
18421837
Editor.isBlock(editor, currentNode))

packages/slate-react/src/hooks/android-input-manager/android-input-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { DebouncedFunc } from 'lodash'
2-
import { Editor, Node, Path, Point, Range, Text, Transforms } from 'slate'
2+
import { Editor, Node, Path, Point, Range, Transforms } from 'slate'
33
import { ReactEditor } from '../../plugin/react-editor'
44
import {
55
applyStringDiff,
@@ -404,7 +404,7 @@ export function createAndroidInputManager({
404404
if (leaf.text.length === start.offset && end.offset === 0) {
405405
const next = Editor.next(editor, {
406406
at: start.path,
407-
match: Text.isText,
407+
match: Node.isText,
408408
})
409409
if (next && Path.equals(next[1], end.path)) {
410410
// when deleting a linebreak, targetRange will span across the break (ie start in the node before and end in the node after)

packages/slate-react/src/hooks/use-children.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useCallback, useRef } from 'react'
2-
import { Ancestor, Editor, Element, DecoratedRange, Text } from 'slate'
2+
import { Ancestor, Editor, Element, DecoratedRange, Text, Node } from 'slate'
33
import { Key, isElementDecorationsEqual } from 'slate-dom'
44
import {
55
RenderChunkProps,
@@ -48,8 +48,7 @@ const useChildren = (props: {
4848
const editor = useSlateStatic()
4949
IS_NODE_MAP_DIRTY.set(editor as ReactEditor, false)
5050

51-
const isEditor = Editor.isEditor(node)
52-
const isBlock = !isEditor && Element.isElement(node) && !editor.isInline(node)
51+
const isBlock = Node.isElement(node) && !editor.isInline(node)
5352
const isLeafBlock = isBlock && Editor.hasInlines(editor, node)
5453
const chunkSize = isLeafBlock ? null : editor.getChunkSize(node)
5554
const chunking = !!chunkSize
@@ -119,7 +118,7 @@ const useChildren = (props: {
119118

120119
if (!chunking) {
121120
return node.children.map((n, i) =>
122-
Text.isText(n) ? renderTextComponent(n, i) : renderElementComponent(n, i)
121+
Node.isText(n) ? renderTextComponent(n, i) : renderElementComponent(n, i)
123122
)
124123
}
125124

packages/slate-react/src/hooks/use-decorations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { createContext, useCallback, useContext, useMemo, useRef } from 'react'
2-
import { DecoratedRange, Descendant, NodeEntry, Text } from 'slate'
2+
import { DecoratedRange, Descendant, Node, NodeEntry } from 'slate'
33
import { isTextDecorationsEqual, isElementDecorationsEqual } from 'slate-dom'
44
import { useSlateStatic } from './use-slate-static'
55
import { ReactEditor } from '../plugin/react-editor'
@@ -31,7 +31,7 @@ export const useDecorations = (
3131
return decorate([node, path])
3232
}
3333

34-
const equalityFn = Text.isText(node)
34+
const equalityFn = Node.isText(node)
3535
? isTextDecorationsEqual
3636
: isElementDecorationsEqual
3737

0 commit comments

Comments
 (0)