Skip to content

Commit

Permalink
Add unstable serialization logic for node JSON parsing (#2157)
Browse files Browse the repository at this point in the history
* Add unstable serialization logic for node JSON parsing

* Ooops

* Freeze editorState

* Migrate code node

* Address feedback

* Address feedback

* Address feedback

* Address more feedback

* Address more feedback

* Address FlowFixMes

* update types

* prettier

* remove import

* polish types

* fix types

* add ut for unstable APIs

* fix rebase issue

* oops

* wip

* more nodes

* types

* prettier

* add tests for core nodes

* update codes.json

* Merge global files

* Rename global type defs

* Update packages/lexical-link/src/index.js

Co-authored-by: Gerard Rovira <zurfyx@users.noreply.github.com>

* fix linter an versions

* more versions

Co-authored-by: acywatson <acy.watson@gmail.com>
Co-authored-by: John Flockton <thegreatercurve@users.noreply.github.com>
Co-authored-by: Gerard Rovira <zurfyx@users.noreply.github.com>
  • Loading branch information
4 people authored May 19, 2022
1 parent 8d54925 commit 8f9a903
Show file tree
Hide file tree
Showing 55 changed files with 1,526 additions and 43 deletions.
2 changes: 2 additions & 0 deletions libdefs/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ declare module '*.jpg' {
const content: any;
export default content;
}

export type Spread<T1, T2> = {[K in Exclude<keyof T1, keyof T2>]: T1[K]} & T2;
23 changes: 23 additions & 0 deletions packages/lexical-code/LexicalCode.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import type {
RangeSelection,
EditorThemeClasses,
LexicalEditor,
SerializedElementNode,
SerializedTextNode,
} from 'lexical';

import {ElementNode, TextNode} from 'lexical';
import {Spread} from 'libdefs/globals';

declare class CodeNode extends ElementNode {
static getType(): string;
Expand All @@ -31,6 +34,8 @@ declare class CodeNode extends ElementNode {
collapseAtStart(): true;
setLanguage(language: string): void;
getLanguage(): string | void;
importJSON(serializedNode: SerializedCodeNode): CodeNode;
exportJSON(): SerializedElementNode;
}
declare function $createCodeNode(language?: string): CodeNode;
declare function $isCodeNode(
Expand Down Expand Up @@ -74,3 +79,21 @@ declare function $isCodeHighlightNode(
): node is CodeHighlightNode;

declare function registerCodeHighlighting(editor: LexicalEditor): () => void;

type SerializedCodeNode = Spread<
{
language: string | null | undefined;
type: 'code';
version: 1;
},
SerializedElementNode
>;

type SerializedCodeHighlightNode = Spread<
{
highlightType: string | null | undefined;
type: 'code-highlight';
version: 1;
},
SerializedTextNode
>;
61 changes: 61 additions & 0 deletions packages/lexical-code/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type {
NodeKey,
ParagraphNode,
RangeSelection,
SerializedElementNode,
SerializedTextNode,
} from 'lexical';

import * as Prism from 'prismjs';
Expand All @@ -39,6 +41,7 @@ import {
mergeRegister,
removeClassNamesFromElement,
} from '@lexical/utils';
import {Spread} from 'globals';
import {
$createLineBreakNode,
$createParagraphNode,
Expand All @@ -59,6 +62,24 @@ import {

const DEFAULT_CODE_LANGUAGE = 'javascript';

type SerializedCodeNode = Spread<
{
language: string | null | undefined;
type: 'code';
version: 1;
},
SerializedElementNode
>;

type SerializedCodeHighlightNode = Spread<
{
highlightType: string | null | undefined;
type: 'code-highlight';
version: 1;
},
SerializedTextNode
>;

const mapToPrismLanguage = (
language: string | null | undefined,
): string | null | undefined => {
Expand Down Expand Up @@ -99,6 +120,11 @@ export class CodeHighlightNode extends TextNode {
);
}

getHighlightType(): string | null | undefined {
const self = this.getLatest<CodeHighlightNode>();
return self.__highlightType;
}

createDOM(config: EditorConfig): HTMLElement {
const element = super.createDOM(config);
const className = getHighlightThemeClass(
Expand Down Expand Up @@ -134,6 +160,25 @@ export class CodeHighlightNode extends TextNode {
return update;
}

static importJSON(
serializedNode: SerializedCodeHighlightNode,
): CodeHighlightNode {
const node = $createCodeHighlightNode(serializedNode.highlightType);
node.setFormat(serializedNode.format);
node.setDetail(serializedNode.detail);
node.setMode(serializedNode.mode);
node.setStyle(serializedNode.style);
return node;
}

exportJSON(): SerializedCodeHighlightNode {
return {
...super.exportJSON(),
highlightType: this.getHighlightType(),
type: 'code-highlight',
};
}

// Prevent formatting (bold, underline, etc)
setFormat(format: number): this {
return this;
Expand Down Expand Up @@ -266,6 +311,22 @@ export class CodeNode extends ElementNode {
};
}

static importJSON(serializedNode: SerializedCodeNode): CodeNode {
const node = $createCodeNode(serializedNode.language);
node.setFormat(serializedNode.format);
node.setIndent(serializedNode.indent);
node.setDirection(serializedNode.direction);
return node;
}

exportJSON(): SerializedCodeNode {
return {
...super.exportJSON(),
language: this.getLanguage(),
type: 'code',
};
}

// Mutation
insertNewAfter(
selection: RangeSelection,
Expand Down
8 changes: 7 additions & 1 deletion packages/lexical-hashtag/LexicalHashtag.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
*
*/

import type {EditorConfig, LexicalNode, NodeKey} from 'lexical';
import type {
EditorConfig,
LexicalNode,
NodeKey,
SerializedTextNode,
} from 'lexical';
import {TextNode} from 'lexical';

export declare class HashtagNode extends TextNode {
Expand All @@ -16,6 +21,7 @@ export declare class HashtagNode extends TextNode {
createDOM(config: EditorConfig): HTMLElement;
canInsertTextBefore(): boolean;
isTextEntity(): true;
static importJSON(serializedNode: SerializedTextNode): HashtagNode;
}
export function $createHashtagNode(text?: string): TextNode;
export function $isHashtagNode(
Expand Down
9 changes: 8 additions & 1 deletion packages/lexical-hashtag/flow/LexicalHashtag.js.flow
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@
* @flow strict
*/

import type {EditorConfig, LexicalNode, NodeKey} from 'lexical';
import type {
EditorConfig,
LexicalNode,
NodeKey,
SerializedTextNode,
} from 'lexical';

import {TextNode} from 'lexical';

declare export class HashtagNode extends TextNode {
static getType(): string;
static clone(node: HashtagNode): HashtagNode;
static importJSON(serializedNode: SerializedTextNode): HashtagNode;
constructor(text: string, key?: NodeKey): void;
createDOM(config: EditorConfig): HTMLElement;
canInsertTextBefore(): boolean;
isTextEntity(): true;
exportJSON(): SerializedTextNode;
}
declare export function $createHashtagNode(text?: string): HashtagNode;
declare export function $isHashtagNode(
Expand Down
23 changes: 22 additions & 1 deletion packages/lexical-hashtag/src/LexicalHashtagNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@
* @flow strict
*/

import type {EditorConfig, LexicalNode, NodeKey} from 'lexical';
import type {
EditorConfig,
LexicalNode,
NodeKey,
SerializedTextNode,
} from 'lexical';

import {addClassNamesToElement} from '@lexical/utils';
import {TextNode} from 'lexical';
Expand All @@ -31,6 +36,22 @@ export class HashtagNode extends TextNode {
return element;
}

static importJSON(serializedNode: SerializedTextNode): HashtagNode {
const node = $createHashtagNode(serializedNode.text);
node.setFormat(serializedNode.format);
node.setDetail(serializedNode.detail);
node.setMode(serializedNode.mode);
node.setStyle(serializedNode.style);
return node;
}

exportJSON(): SerializedTextNode {
return {
...super.exportJSON(),
type: 'hashtag',
};
}

canInsertTextBefore(): boolean {
return false;
}
Expand Down
55 changes: 55 additions & 0 deletions packages/lexical-link/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,20 @@ import type {
LexicalNode,
NodeKey,
RangeSelection,
SerializedElementNode,
} from 'lexical';

import {addClassNamesToElement} from '@lexical/utils';
import {$isElementNode, createCommand, ElementNode} from 'lexical';
import invariant from 'shared/invariant';

export type SerializedLinkNode = {
...SerializedElementNode,
type: 'link',
url: string,
version: 1,
...
};

export class LinkNode extends ElementNode {
__url: string;
Expand Down Expand Up @@ -67,6 +77,22 @@ export class LinkNode extends ElementNode {
};
}

static importJSON(serializedNode: SerializedLinkNode): LinkNode {
const node = $createLinkNode(serializedNode.url);
node.setFormat(serializedNode.format);
node.setIndent(serializedNode.indent);
node.setDirection(serializedNode.direction);
return node;
}

exportJSON(): SerializedElementNode {
return {
...super.exportJSON(),
type: 'link',
url: this.getURL(),
};
}

getURL(): string {
return this.getLatest().__url;
}
Expand Down Expand Up @@ -119,6 +145,13 @@ export function $isLinkNode(node: ?LexicalNode): boolean %checks {
return node instanceof LinkNode;
}

export type SerializedAutoLinkNode = {
...SerializedLinkNode,
type: 'autolink',
version: 1,
...
};

// Custom node type to override `canInsertTextAfter` that will
// allow typing within the link
export class AutoLinkNode extends LinkNode {
Expand All @@ -131,6 +164,28 @@ export class AutoLinkNode extends LinkNode {
return new AutoLinkNode(node.__url, node.__key);
}

static importJSON(
serializedNode: SerializedLinkNode | SerializedAutoLinkNode,
): AutoLinkNode {
invariant(
serializedNode.type !== 'autolink',
'Incorrect node type received in importJSON for %s',
this.getType(),
);
const node = $createAutoLinkNode(serializedNode.url);
node.setFormat(serializedNode.format);
node.setIndent(serializedNode.indent);
node.setDirection(serializedNode.direction);
return node;
}

exportJSON(): SerializedElementNode {
return {
...super.exportJSON(),
type: 'autolink',
};
}

insertNewAfter(selection: RangeSelection): null | ElementNode {
const element = this.getParentOrThrow().insertNewAfter(selection);
if ($isElementNode(element)) {
Expand Down
26 changes: 26 additions & 0 deletions packages/lexical-list/LexicalList.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
*/

import {ListNodeTagType} from './src/LexicalListNode';
import {Spread} from 'globals';
import {
ElementNode,
LexicalNode,
LexicalEditor,
ParagraphNode,
RangeSelection,
LexicalCommand,
SerializedElementNode,
} from 'lexical';

export type ListType = 'number' | 'bullet' | 'check';
Expand All @@ -40,17 +42,41 @@ export declare class ListItemNode extends ElementNode {
getChecked(): boolean | void;
setChecked(boolean): this;
toggleChecked(): void;
static importJSON(serializedNode: SerializedListItemNode): ListItemNode;
exportJSON(): SerializedListItemNode;
}
export declare class ListNode extends ElementNode {
canBeEmpty(): false;
append(...nodesToAppend: LexicalNode[]): ListNode;
getTag(): ListNodeTagType;
getListType(): ListType;
static importJSON(serializedNode: SerializedListNode): ListNode;
exportJSON(): SerializedListNode;
}

export function outdentList(): void;
export function removeList(editor: LexicalEditor): boolean;

export var INSERT_UNORDERED_LIST_COMMAND: LexicalCommand<void>;
export var INSERT_ORDERED_LIST_COMMAND: LexicalCommand<void>;
export var INSERT_CHECK_LIST_COMMAND: LexicalCommand<void>;
export var REMOVE_LIST_COMMAND: LexicalCommand<void>;

export type SerializedListItemNode = Spread<
{
checked: boolean | void;
value: number;
type: 'listitem';
},
SerializedElementNode
>;

export type SerializedListNode = Spread<
{
listType: ListType;
start: number;
tag: ListNodeTagType;
type: 'list';
},
SerializedElementNode
>;
Loading

2 comments on commit 8f9a903

@vercel
Copy link

@vercel vercel bot commented on 8f9a903 May 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

lexical – ./packages/lexical-website-new

lexical-fbopensource.vercel.app
lexical-git-main-fbopensource.vercel.app
lexical.dev
lexicaljs.com
lexicaljs.org
www.lexical.dev

@vercel
Copy link

@vercel vercel bot commented on 8f9a903 May 19, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

lexical-playground – ./packages/lexical-playground

playground.lexical.dev
lexical-playground-fbopensource.vercel.app
lexical-playground.vercel.app
lexical-playground-git-main-fbopensource.vercel.app

Please sign in to comment.