diff --git a/.changeset/purple-tigers-leave.md b/.changeset/purple-tigers-leave.md new file mode 100644 index 0000000000..d72d80534f --- /dev/null +++ b/.changeset/purple-tigers-leave.md @@ -0,0 +1,5 @@ +--- +'slate': patch +--- + +Removed the last traces of immer and eliminated it as a dependency diff --git a/packages/slate/package.json b/packages/slate/package.json index 3f42a8bb7c..fe598152bb 100644 --- a/packages/slate/package.json +++ b/packages/slate/package.json @@ -13,10 +13,6 @@ "files": [ "dist/" ], - "dependencies": { - "immer": "^10.0.3", - "tiny-warning": "^1.0.3" - }, "devDependencies": { "@babel/runtime": "^7.23.2", "lodash": "^4.17.21", diff --git a/packages/slate/src/interfaces/node.ts b/packages/slate/src/interfaces/node.ts index 89b2aad9f0..d461079e48 100644 --- a/packages/slate/src/interfaces/node.ts +++ b/packages/slate/src/interfaces/node.ts @@ -1,6 +1,6 @@ -import { produce } from 'immer' import { Editor, Path, Range, Scrubber, Text } from '..' import { Element, ElementEntry } from './element' +import { modifyChildren, modifyLeaf, removeChildren } from '../utils/modify' /** * The `Node` union type represents all of the different types of nodes that @@ -357,35 +357,37 @@ export const Node: NodeInterface = { }, fragment(root: T, range: Range): T['children'] { - const newRoot = produce({ children: root.children }, r => { - const [start, end] = Range.edges(range) - const nodeEntries = Node.nodes(r, { - reverse: true, - pass: ([, path]) => !Range.includes(range, path), - }) - - for (const [, path] of nodeEntries) { - if (!Range.includes(range, path)) { - const parent = Node.parent(r, path) - const index = path[path.length - 1] - parent.children.splice(index, 1) - } + const newRoot = { children: root.children } - if (Path.equals(path, end.path)) { - const leaf = Node.leaf(r, path) - leaf.text = leaf.text.slice(0, end.offset) - } + const [start, end] = Range.edges(range) + const nodeEntries = Node.nodes(newRoot, { + reverse: true, + pass: ([, path]) => !Range.includes(range, path), + }) - if (Path.equals(path, start.path)) { - const leaf = Node.leaf(r, path) - leaf.text = leaf.text.slice(start.offset) - } + for (const [, path] of nodeEntries) { + if (!Range.includes(range, path)) { + const index = path[path.length - 1] + + modifyChildren(newRoot, Path.parent(path), children => + removeChildren(children, index, 1) + ) } - if (Editor.isEditor(r)) { - r.selection = null + if (Path.equals(path, end.path)) { + modifyLeaf(newRoot, path, node => { + const before = node.text.slice(0, end.offset) + return { ...node, text: before } + }) } - }) + + if (Path.equals(path, start.path)) { + modifyLeaf(newRoot, path, node => { + const before = node.text.slice(start.offset) + return { ...node, text: before } + }) + } + } return newRoot.children }, diff --git a/packages/slate/src/interfaces/transforms/general.ts b/packages/slate/src/interfaces/transforms/general.ts index d2e44c54db..a198c8a6fe 100644 --- a/packages/slate/src/interfaces/transforms/general.ts +++ b/packages/slate/src/interfaces/transforms/general.ts @@ -1,5 +1,4 @@ import { - Ancestor, Descendant, Editor, Element, @@ -13,6 +12,14 @@ import { Selection, Text, } from '../../index' +import { + insertChildren, + modifyChildren, + modifyDescendant, + modifyLeaf, + removeChildren, + replaceChildren, +} from '../../utils/modify' export interface GeneralTransforms { /** @@ -21,92 +28,6 @@ export interface GeneralTransforms { transform: (editor: Editor, op: Operation) => void } -const insertChildren = (xs: T[], index: number, ...newValues: T[]) => [ - ...xs.slice(0, index), - ...newValues, - ...xs.slice(index), -] - -const replaceChildren = ( - xs: T[], - index: number, - removeCount: number, - ...newValues: T[] -) => [...xs.slice(0, index), ...newValues, ...xs.slice(index + removeCount)] - -const removeChildren = replaceChildren - -/** - * Replace a descendant with a new node, replacing all ancestors - */ -const modifyDescendant = ( - editor: Editor, - path: Path, - f: (node: N) => N -) => { - if (path.length === 0) { - throw new Error('Cannot modify the editor') - } - - const node = Node.get(editor, path) as N - const slicedPath = path.slice() - let modifiedNode: Node = f(node) - - while (slicedPath.length > 1) { - const index = slicedPath.pop()! - const ancestorNode = Node.get(editor, slicedPath) as Ancestor - - modifiedNode = { - ...ancestorNode, - children: replaceChildren(ancestorNode.children, index, 1, modifiedNode), - } - } - - const index = slicedPath.pop()! - editor.children = replaceChildren(editor.children, index, 1, modifiedNode) -} - -/** - * Replace the children of a node, replacing all ancestors - */ -const modifyChildren = ( - editor: Editor, - path: Path, - f: (children: Descendant[]) => Descendant[] -) => { - if (path.length === 0) { - editor.children = f(editor.children) - } else { - modifyDescendant(editor, path, node => { - if (Text.isText(node)) { - throw new Error( - `Cannot get the element at path [${path}] because it refers to a leaf node: ${Scrubber.stringify( - node - )}` - ) - } - - return { ...node, children: f(node.children) } - }) - } -} - -/** - * Replace a leaf, replacing all ancestors - */ -const modifyLeaf = (editor: Editor, path: Path, f: (leaf: Text) => Text) => - modifyDescendant(editor, path, node => { - if (!Text.isText(node)) { - throw new Error( - `Cannot get the leaf node at path [${path}] because it refers to a non-leaf node: ${Scrubber.stringify( - node - )}` - ) - } - - return f(node) - }) - // eslint-disable-next-line no-redeclare export const GeneralTransforms: GeneralTransforms = { transform(editor: Editor, op: Operation): void { diff --git a/packages/slate/src/utils/modify.ts b/packages/slate/src/utils/modify.ts new file mode 100644 index 0000000000..2e504c0bf4 --- /dev/null +++ b/packages/slate/src/utils/modify.ts @@ -0,0 +1,99 @@ +import { + Ancestor, + Descendant, + Element, + Node, + Path, + Scrubber, + Text, +} from '../interfaces' + +export const insertChildren = ( + xs: T[], + index: number, + ...newValues: T[] +) => [...xs.slice(0, index), ...newValues, ...xs.slice(index)] + +export const replaceChildren = ( + xs: T[], + index: number, + removeCount: number, + ...newValues: T[] +) => [...xs.slice(0, index), ...newValues, ...xs.slice(index + removeCount)] + +export const removeChildren = replaceChildren + +/** + * Replace a descendant with a new node, replacing all ancestors + */ +export const modifyDescendant = ( + root: Ancestor, + path: Path, + f: (node: N) => N +) => { + if (path.length === 0) { + throw new Error('Cannot modify the editor') + } + + const node = Node.get(root, path) as N + const slicedPath = path.slice() + let modifiedNode: Node = f(node) + + while (slicedPath.length > 1) { + const index = slicedPath.pop()! + const ancestorNode = Node.get(root, slicedPath) as Ancestor + + modifiedNode = { + ...ancestorNode, + children: replaceChildren(ancestorNode.children, index, 1, modifiedNode), + } + } + + const index = slicedPath.pop()! + root.children = replaceChildren(root.children, index, 1, modifiedNode) +} + +/** + * Replace the children of a node, replacing all ancestors + */ +export const modifyChildren = ( + root: Ancestor, + path: Path, + f: (children: Descendant[]) => Descendant[] +) => { + if (path.length === 0) { + root.children = f(root.children) + } else { + modifyDescendant(root, path, node => { + if (Text.isText(node)) { + throw new Error( + `Cannot get the element at path [${path}] because it refers to a leaf node: ${Scrubber.stringify( + node + )}` + ) + } + + return { ...node, children: f(node.children) } + }) + } +} + +/** + * Replace a leaf, replacing all ancestors + */ +export const modifyLeaf = ( + root: Ancestor, + path: Path, + f: (leaf: Text) => Text +) => + modifyDescendant(root, path, node => { + if (!Text.isText(node)) { + throw new Error( + `Cannot get the leaf node at path [${path}] because it refers to a non-leaf node: ${Scrubber.stringify( + node + )}` + ) + } + + return f(node) + }) diff --git a/yarn.lock b/yarn.lock index 35d30d2153..8a1ec8e338 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8228,13 +8228,6 @@ __metadata: languageName: node linkType: hard -"immer@npm:^10.0.3": - version: 10.0.3 - resolution: "immer@npm:10.0.3" - checksum: 0be07be2f278bd1988112613648e0cf9a64fc316d5b4817f273b519fbfed0f1714275b041911f0b8c560c199b2e3430824ce620c23262c96c9d4efc9909ff1cc - languageName: node - linkType: hard - "import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" @@ -13714,11 +13707,9 @@ __metadata: resolution: "slate@workspace:packages/slate" dependencies: "@babel/runtime": "npm:^7.23.2" - immer: "npm:^10.0.3" lodash: "npm:^4.17.21" slate-hyperscript: "npm:^0.115.0" source-map-loader: "npm:^4.0.1" - tiny-warning: "npm:^1.0.3" languageName: unknown linkType: soft @@ -14485,13 +14476,6 @@ __metadata: languageName: node linkType: hard -"tiny-warning@npm:^1.0.3": - version: 1.0.3 - resolution: "tiny-warning@npm:1.0.3" - checksum: da62c4acac565902f0624b123eed6dd3509bc9a8d30c06e017104bedcf5d35810da8ff72864400ad19c5c7806fc0a8323c68baf3e326af7cb7d969f846100d71 - languageName: node - linkType: hard - "titleize@npm:^3.0.0": version: 3.0.0 resolution: "titleize@npm:3.0.0"