@lexical/html 0.44.1-nightly.20260518.0 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/{DOMRenderExtension.d.ts → dist/DOMRenderExtension.d.ts} +12 -1
- package/dist/DOMRenderRuntime.d.ts +51 -0
- package/dist/LexicalHtml.dev.js +3192 -0
- package/dist/LexicalHtml.dev.mjs +3146 -0
- package/{LexicalHtml.js.flow → dist/LexicalHtml.js.flow} +16 -16
- package/dist/LexicalHtml.mjs +56 -0
- package/dist/LexicalHtml.node.mjs +54 -0
- package/dist/LexicalHtml.prod.js +9 -0
- package/dist/LexicalHtml.prod.mjs +9 -0
- package/dist/RenderContext.d.ts +68 -0
- package/{compileDOMRenderConfigOverrides.d.ts → dist/compileDOMRenderConfigOverrides.d.ts} +1 -1
- package/{constants.d.ts → dist/constants.d.ts} +2 -0
- package/dist/domOverride.d.ts +23 -0
- package/dist/import/CoreImportExtension.d.ts +11 -0
- package/dist/import/DOMImportExtension.d.ts +82 -0
- package/dist/import/HorizontalRuleImportExtension.d.ts +27 -0
- package/dist/import/ImportContext.d.ts +208 -0
- package/dist/import/compileImportRules.d.ts +50 -0
- package/dist/import/coreImportRules.d.ts +25 -0
- package/dist/import/defineImportRule.d.ts +32 -0
- package/dist/import/defineOverlayRules.d.ts +66 -0
- package/dist/import/index.d.ts +38 -0
- package/dist/import/inlineStylesFromStyleSheets.d.ts +28 -0
- package/dist/import/parseCss.d.ts +18 -0
- package/dist/import/runImport.d.ts +19 -0
- package/dist/import/schemas.d.ts +91 -0
- package/dist/import/sel.d.ts +74 -0
- package/dist/import/types.d.ts +394 -0
- package/dist/index.d.ts +44 -0
- package/{types.d.ts → dist/types.d.ts} +96 -8
- package/package.json +33 -18
- package/src/ContextRecord.ts +243 -0
- package/src/DOMRenderExtension.ts +96 -0
- package/src/DOMRenderRuntime.ts +265 -0
- package/src/RenderContext.ts +168 -0
- package/src/compileDOMRenderConfigOverrides.ts +416 -0
- package/src/constants.ts +18 -0
- package/src/domOverride.ts +46 -0
- package/src/import/CoreImportExtension.ts +26 -0
- package/src/import/DOMImportExtension.ts +221 -0
- package/src/import/HorizontalRuleImportExtension.ts +53 -0
- package/src/import/ImportContext.ts +339 -0
- package/src/import/compileImportRules.ts +178 -0
- package/src/import/coreImportRules.ts +485 -0
- package/src/import/defineImportRule.ts +40 -0
- package/src/import/defineOverlayRules.ts +105 -0
- package/src/import/index.ts +96 -0
- package/src/import/inlineStylesFromStyleSheets.ts +104 -0
- package/src/import/parseCss.ts +219 -0
- package/src/import/runImport.ts +245 -0
- package/src/import/schemas.ts +236 -0
- package/src/import/sel.ts +314 -0
- package/src/import/types.ts +471 -0
- package/src/index.ts +555 -0
- package/src/types.ts +470 -0
- package/LexicalHtml.dev.js +0 -914
- package/LexicalHtml.dev.mjs +0 -900
- package/LexicalHtml.mjs +0 -24
- package/LexicalHtml.node.mjs +0 -22
- package/LexicalHtml.prod.js +0 -9
- package/LexicalHtml.prod.mjs +0 -9
- package/RenderContext.d.ts +0 -32
- package/domOverride.d.ts +0 -18
- package/index.d.ts +0 -32
- /package/{ContextRecord.d.ts → dist/ContextRecord.d.ts} +0 -0
- /package/{LexicalHtml.js → dist/LexicalHtml.js} +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
import type {EditorDOMRenderConfig} from 'lexical';
|
|
9
|
+
|
|
10
|
+
import {getPeerDependencyFromEditor} from '@lexical/extension';
|
|
11
|
+
import {$getEditor, $getEditorDOMRenderConfig, LexicalEditor} from 'lexical';
|
|
12
|
+
|
|
13
|
+
import {DOMRenderContextSymbol, DOMRenderExtensionName} from './constants';
|
|
14
|
+
import {
|
|
15
|
+
$withContext,
|
|
16
|
+
createContextState,
|
|
17
|
+
getContextRecord,
|
|
18
|
+
getContextValue,
|
|
19
|
+
} from './ContextRecord';
|
|
20
|
+
import {DOMRenderExtension} from './DOMRenderExtension';
|
|
21
|
+
import {
|
|
22
|
+
AnyRenderStateConfigPairOrUpdater,
|
|
23
|
+
ContextRecord,
|
|
24
|
+
RenderStateConfig,
|
|
25
|
+
} from './types';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Create a context state to be used during render.
|
|
29
|
+
*
|
|
30
|
+
* Note that to support the ValueOrUpdater pattern you can not use a
|
|
31
|
+
* function for V (but you may wrap it in an array or object).
|
|
32
|
+
*
|
|
33
|
+
* @experimental
|
|
34
|
+
* @__NO_SIDE_EFFECTS__
|
|
35
|
+
*/
|
|
36
|
+
export function createRenderState<V>(
|
|
37
|
+
name: string,
|
|
38
|
+
getDefaultValue: () => V,
|
|
39
|
+
isEqual?: (a: V, b: V) => boolean,
|
|
40
|
+
): RenderStateConfig<V> {
|
|
41
|
+
return createContextState(
|
|
42
|
+
DOMRenderContextSymbol,
|
|
43
|
+
name,
|
|
44
|
+
getDefaultValue,
|
|
45
|
+
isEqual,
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Render context state that is true if the export was initiated from the root of the document.
|
|
51
|
+
* @experimental
|
|
52
|
+
*/
|
|
53
|
+
export const RenderContextRoot = createRenderState('root', Boolean);
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Render context state that is true if this is an export operation ($generateHtmlFromNodes).
|
|
57
|
+
* @experimental
|
|
58
|
+
*/
|
|
59
|
+
export const RenderContextExport = createRenderState('isExport', Boolean);
|
|
60
|
+
|
|
61
|
+
function getDefaultRenderContext(
|
|
62
|
+
editor: LexicalEditor,
|
|
63
|
+
): undefined | ContextRecord<typeof DOMRenderContextSymbol> {
|
|
64
|
+
const dep = getPeerDependencyFromEditor<typeof DOMRenderExtension>(
|
|
65
|
+
editor,
|
|
66
|
+
DOMRenderExtensionName,
|
|
67
|
+
);
|
|
68
|
+
return dep ? dep.output.defaults : undefined;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function getRenderContext(
|
|
72
|
+
editor: LexicalEditor,
|
|
73
|
+
): undefined | ContextRecord<typeof DOMRenderContextSymbol> {
|
|
74
|
+
return (
|
|
75
|
+
getContextRecord(DOMRenderContextSymbol, editor) ||
|
|
76
|
+
getDefaultRenderContext(editor)
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Get a render context value during a DOM render or export operation.
|
|
82
|
+
* @experimental
|
|
83
|
+
*/
|
|
84
|
+
export function $getRenderContextValue<V>(
|
|
85
|
+
cfg: RenderStateConfig<V>,
|
|
86
|
+
editor: LexicalEditor = $getEditor(),
|
|
87
|
+
): V {
|
|
88
|
+
return getContextValue(getRenderContext(editor), cfg);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getRuntime(editor: LexicalEditor) {
|
|
92
|
+
const dep = getPeerDependencyFromEditor<typeof DOMRenderExtension>(
|
|
93
|
+
editor,
|
|
94
|
+
DOMRenderExtensionName,
|
|
95
|
+
);
|
|
96
|
+
return dep ? dep.output.runtime : undefined;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Imperatively set a value in the persistent editor render context.
|
|
101
|
+
*
|
|
102
|
+
* Unlike {@link $withRenderContext} (which scopes values to a callback), this
|
|
103
|
+
* persists on the editor. If the change flips any override's
|
|
104
|
+
* `disabledForEditor` result, the resident render config is recompiled and the
|
|
105
|
+
* affected nodes are re-rendered. No-op if {@link DOMRenderExtension} is not
|
|
106
|
+
* installed.
|
|
107
|
+
*
|
|
108
|
+
* @experimental
|
|
109
|
+
*/
|
|
110
|
+
export function $setRenderContextValue<V>(
|
|
111
|
+
cfg: RenderStateConfig<V>,
|
|
112
|
+
value: V,
|
|
113
|
+
editor: LexicalEditor = $getEditor(),
|
|
114
|
+
): void {
|
|
115
|
+
const runtime = getRuntime(editor);
|
|
116
|
+
if (runtime) {
|
|
117
|
+
runtime.setContextValue(cfg, value);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Imperatively update a value in the persistent editor render context with an
|
|
123
|
+
* updater function. See {@link $setRenderContextValue}.
|
|
124
|
+
*
|
|
125
|
+
* @experimental
|
|
126
|
+
*/
|
|
127
|
+
export function $updateRenderContextValue<V>(
|
|
128
|
+
cfg: RenderStateConfig<V>,
|
|
129
|
+
updater: (prev: V) => V,
|
|
130
|
+
editor: LexicalEditor = $getEditor(),
|
|
131
|
+
): void {
|
|
132
|
+
const runtime = getRuntime(editor);
|
|
133
|
+
if (runtime) {
|
|
134
|
+
runtime.setContextValue(
|
|
135
|
+
cfg,
|
|
136
|
+
updater(getContextValue(runtime.editorContext, cfg)),
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Resolve the {@link EditorDOMRenderConfig} to use for the current
|
|
143
|
+
* export/generate session, applying any `disabledForSession` overrides against
|
|
144
|
+
* the active session context. Falls back to the editor's resident config when
|
|
145
|
+
* {@link DOMRenderExtension} is not installed.
|
|
146
|
+
*
|
|
147
|
+
* @experimental
|
|
148
|
+
*/
|
|
149
|
+
export function $getSessionDOMRenderConfig(
|
|
150
|
+
editor: LexicalEditor = $getEditor(),
|
|
151
|
+
): EditorDOMRenderConfig {
|
|
152
|
+
const runtime = getRuntime(editor);
|
|
153
|
+
return runtime
|
|
154
|
+
? runtime.getSessionConfig()
|
|
155
|
+
: $getEditorDOMRenderConfig(editor);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Execute a callback within a render context with the given config pairs.
|
|
160
|
+
* @experimental
|
|
161
|
+
*/
|
|
162
|
+
export const $withRenderContext: (
|
|
163
|
+
cfg: readonly AnyRenderStateConfigPairOrUpdater[],
|
|
164
|
+
editor?: LexicalEditor,
|
|
165
|
+
) => <T>(f: () => T) => T = $withContext(
|
|
166
|
+
DOMRenderContextSymbol,
|
|
167
|
+
getDefaultRenderContext,
|
|
168
|
+
);
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
import {getKnownTypesAndNodes} from '@lexical/extension';
|
|
9
|
+
import invariant from '@lexical/internal/invariant';
|
|
10
|
+
import {
|
|
11
|
+
$isLexicalNode,
|
|
12
|
+
DEFAULT_EDITOR_DOM_CONFIG,
|
|
13
|
+
type EditorDOMRenderConfig,
|
|
14
|
+
getStaticNodeConfig,
|
|
15
|
+
InitialEditorConfig,
|
|
16
|
+
Klass,
|
|
17
|
+
LexicalEditor,
|
|
18
|
+
type LexicalNode,
|
|
19
|
+
} from 'lexical';
|
|
20
|
+
|
|
21
|
+
import {ALWAYS_TRUE} from './constants';
|
|
22
|
+
import {AnyDOMRenderMatch, DOMRenderConfig, DOMRenderMatch} from './types';
|
|
23
|
+
|
|
24
|
+
interface TypeRecord {
|
|
25
|
+
readonly klass: Klass<LexicalNode>;
|
|
26
|
+
readonly types: {[NodeAndSubclasses in string]?: boolean};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type TypeTree = {
|
|
30
|
+
[NodeType in string]?: TypeRecord;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function buildTypeTree(
|
|
34
|
+
editorConfig: Pick<InitialEditorConfig, 'nodes'>,
|
|
35
|
+
): TypeTree {
|
|
36
|
+
const t: TypeTree = {};
|
|
37
|
+
const {nodes} = getKnownTypesAndNodes(editorConfig);
|
|
38
|
+
for (const klass of nodes) {
|
|
39
|
+
const type = klass.getType();
|
|
40
|
+
t[type] = {klass, types: {}};
|
|
41
|
+
}
|
|
42
|
+
for (const baseRec of Object.values(t)) {
|
|
43
|
+
if (baseRec) {
|
|
44
|
+
const baseType = baseRec.klass.getType();
|
|
45
|
+
for (
|
|
46
|
+
let {klass} = baseRec;
|
|
47
|
+
$isLexicalNode(klass.prototype);
|
|
48
|
+
klass = Object.getPrototypeOf(klass)
|
|
49
|
+
) {
|
|
50
|
+
const {ownNodeType} = getStaticNodeConfig(klass);
|
|
51
|
+
const superRec = ownNodeType && t[ownNodeType];
|
|
52
|
+
if (superRec) {
|
|
53
|
+
superRec.types[baseType] = true;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return t;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
type PredicateOrTypes =
|
|
62
|
+
| ((node: LexicalNode) => boolean)
|
|
63
|
+
| {[NodeType in string]?: true};
|
|
64
|
+
type TypeRender<T> = {[NodeType in string]?: T[]};
|
|
65
|
+
type AnyRender<T> =
|
|
66
|
+
| readonly [(node: LexicalNode) => boolean, T]
|
|
67
|
+
| readonly ['types', TypeRender<T>];
|
|
68
|
+
|
|
69
|
+
type PreEditorDOMRenderConfig = {
|
|
70
|
+
[K in keyof EditorDOMRenderConfig]: AnyRender<AnyDOMRenderMatch[K]>[];
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
function buildNodePredicate<T extends LexicalNode>(klass: Klass<T>) {
|
|
74
|
+
return (node: LexicalNode): node is T => node instanceof klass;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function getPredicate(
|
|
78
|
+
typeTree: TypeTree,
|
|
79
|
+
{nodes}: DOMRenderMatch<LexicalNode>,
|
|
80
|
+
): {[NodeType in string]?: true} | ((node: LexicalNode) => boolean) {
|
|
81
|
+
if (nodes === '*') {
|
|
82
|
+
return ALWAYS_TRUE;
|
|
83
|
+
}
|
|
84
|
+
let types: undefined | {[NodeType in string]?: true} = {};
|
|
85
|
+
const predicates: ((node: LexicalNode) => boolean)[] = [];
|
|
86
|
+
for (const klassOrPredicate of nodes) {
|
|
87
|
+
if ('getType' in klassOrPredicate) {
|
|
88
|
+
const type = klassOrPredicate.getType();
|
|
89
|
+
if (types) {
|
|
90
|
+
const tree = typeTree[type];
|
|
91
|
+
invariant(
|
|
92
|
+
tree !== undefined,
|
|
93
|
+
'Node class %s with type %s not registered in editor',
|
|
94
|
+
klassOrPredicate.name,
|
|
95
|
+
type,
|
|
96
|
+
);
|
|
97
|
+
types = Object.assign(types, tree.types);
|
|
98
|
+
}
|
|
99
|
+
predicates.push(buildNodePredicate(klassOrPredicate));
|
|
100
|
+
} else {
|
|
101
|
+
types = undefined;
|
|
102
|
+
predicates.push(klassOrPredicate);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (types) {
|
|
106
|
+
return types;
|
|
107
|
+
} else if (predicates.length === 1) {
|
|
108
|
+
return predicates[0];
|
|
109
|
+
}
|
|
110
|
+
return (node: LexicalNode): boolean => {
|
|
111
|
+
for (const predicate of predicates) {
|
|
112
|
+
if (predicate(node)) {
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function makePrerender(): PreEditorDOMRenderConfig {
|
|
121
|
+
return {
|
|
122
|
+
$createDOM: [],
|
|
123
|
+
$decorateDOM: [],
|
|
124
|
+
$exportDOM: [],
|
|
125
|
+
$extractWithChild: [],
|
|
126
|
+
$getDOMSlot: [],
|
|
127
|
+
$shouldExclude: [],
|
|
128
|
+
$shouldInclude: [],
|
|
129
|
+
$updateDOM: [],
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
134
|
+
type AccFn<T, N extends LexicalNode, Args extends any[]> = (
|
|
135
|
+
node: N,
|
|
136
|
+
...rest: [...Args, editor: LexicalEditor]
|
|
137
|
+
) => T;
|
|
138
|
+
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
140
|
+
type GetOverrideFn<T, N extends LexicalNode, Args extends any[]> = (
|
|
141
|
+
n: N,
|
|
142
|
+
) => undefined | OverrideFn<T, N, Args>;
|
|
143
|
+
|
|
144
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
145
|
+
type OverrideFn<T, N extends LexicalNode, Args extends any[]> = (
|
|
146
|
+
node: N,
|
|
147
|
+
...rest: [...Args, $next: () => T, editor: LexicalEditor]
|
|
148
|
+
) => T;
|
|
149
|
+
|
|
150
|
+
function ignoreNext2<T, N extends LexicalNode>(
|
|
151
|
+
acc: AccFn<T, N, []>,
|
|
152
|
+
): OverrideFn<T, N, []> {
|
|
153
|
+
return (node: N, _$next: () => T, editor: LexicalEditor) => acc(node, editor);
|
|
154
|
+
}
|
|
155
|
+
function ignoreNext3<T, N extends LexicalNode, A>(
|
|
156
|
+
acc: AccFn<T, N, [A]>,
|
|
157
|
+
): OverrideFn<T, N, [A]> {
|
|
158
|
+
return (node: N, a: A, _$next: () => T, editor: LexicalEditor) =>
|
|
159
|
+
acc(node, a, editor);
|
|
160
|
+
}
|
|
161
|
+
function ignoreNext4<T, N extends LexicalNode, A, B>(
|
|
162
|
+
acc: AccFn<T, N, [A, B]>,
|
|
163
|
+
): OverrideFn<T, N, [A, B]> {
|
|
164
|
+
return (node: N, a: A, b: B, _$next: () => T, editor: LexicalEditor) =>
|
|
165
|
+
acc(node, a, b, editor);
|
|
166
|
+
}
|
|
167
|
+
function ignoreNext5<T, N extends LexicalNode, A, B, C>(
|
|
168
|
+
acc: AccFn<T, N, [A, B, C]>,
|
|
169
|
+
): OverrideFn<T, N, [A, B, C]> {
|
|
170
|
+
return (node: N, a: A, b: B, c: C, _$next: () => T, editor: LexicalEditor) =>
|
|
171
|
+
acc(node, a, b, c, editor);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function merge2<T, N extends LexicalNode>(
|
|
175
|
+
$acc: AccFn<T, N, []>,
|
|
176
|
+
$getOverride: GetOverrideFn<T, N, []>,
|
|
177
|
+
): typeof $acc {
|
|
178
|
+
return (node, editor) => {
|
|
179
|
+
const $next = () => $acc(node, editor);
|
|
180
|
+
const $override = $getOverride(node);
|
|
181
|
+
return $override ? $override(node, $next, editor) : $next();
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function merge3<T, N extends LexicalNode, A>(
|
|
186
|
+
acc: AccFn<T, N, [A]>,
|
|
187
|
+
$getOverride: GetOverrideFn<T, N, [A]>,
|
|
188
|
+
): typeof acc {
|
|
189
|
+
return (node, a, editor) => {
|
|
190
|
+
const $next = () => acc(node, a, editor);
|
|
191
|
+
const $override = $getOverride(node);
|
|
192
|
+
return $override ? $override(node, a, $next, editor) : $next();
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const merge3GetDOMSlot = merge3 as (
|
|
197
|
+
acc: EditorDOMRenderConfig['$getDOMSlot'],
|
|
198
|
+
$getOverride: (n: LexicalNode) => DOMRenderMatch<LexicalNode>['$getDOMSlot'],
|
|
199
|
+
) => EditorDOMRenderConfig['$getDOMSlot'];
|
|
200
|
+
|
|
201
|
+
const ignoreNext3GetDOMSlot = ignoreNext3 as (
|
|
202
|
+
fn: EditorDOMRenderConfig['$getDOMSlot'],
|
|
203
|
+
) => DOMRenderMatch<LexicalNode>['$getDOMSlot'];
|
|
204
|
+
|
|
205
|
+
function merge4<T, N extends LexicalNode, A, B>(
|
|
206
|
+
$acc: AccFn<T, N, [A, B]>,
|
|
207
|
+
$getOverride: GetOverrideFn<T, N, [A, B]>,
|
|
208
|
+
): typeof $acc {
|
|
209
|
+
return (node, a, b, editor) => {
|
|
210
|
+
const $next = () => $acc(node, a, b, editor);
|
|
211
|
+
const $override = $getOverride(node);
|
|
212
|
+
return $override ? $override(node, a, b, $next, editor) : $next();
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function merge5<T, N extends LexicalNode, A, B, C>(
|
|
217
|
+
acc: AccFn<T, N, [A, B, C]>,
|
|
218
|
+
$getOverride: GetOverrideFn<T, N, [A, B, C]>,
|
|
219
|
+
): typeof acc {
|
|
220
|
+
return (node, a, b, c, editor) => {
|
|
221
|
+
const $next = () => acc(node, a, b, c, editor);
|
|
222
|
+
const $override = $getOverride(node);
|
|
223
|
+
return $override ? $override(node, a, b, c, $next, editor) : $next();
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function sequence4<N extends LexicalNode, A, B>(
|
|
228
|
+
$acc: AccFn<void, N, [A, B]>,
|
|
229
|
+
$getOverride: (n: N) => undefined | AccFn<void, N, [A, B]>,
|
|
230
|
+
): typeof $acc {
|
|
231
|
+
return (node, a, b, editor) => {
|
|
232
|
+
$acc(node, a, b, editor);
|
|
233
|
+
const $override = $getOverride(node);
|
|
234
|
+
if ($override) {
|
|
235
|
+
$override(node, a, b, editor);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function compilePrerenderKey<K extends keyof PreEditorDOMRenderConfig>(
|
|
241
|
+
prerender: PreEditorDOMRenderConfig,
|
|
242
|
+
k: K,
|
|
243
|
+
defaults: EditorDOMRenderConfig,
|
|
244
|
+
mergeFunction: (
|
|
245
|
+
$acc: EditorDOMRenderConfig[K],
|
|
246
|
+
$getOverride: (node: LexicalNode) => AnyDOMRenderMatch[K],
|
|
247
|
+
) => typeof $acc,
|
|
248
|
+
ignoreNextFunction: (fn: EditorDOMRenderConfig[K]) => AnyDOMRenderMatch[K],
|
|
249
|
+
): void {
|
|
250
|
+
let acc = defaults[k];
|
|
251
|
+
for (const pair of prerender[k]) {
|
|
252
|
+
if (typeof pair[0] === 'function') {
|
|
253
|
+
const [$predicate, $override] = pair;
|
|
254
|
+
acc = mergeFunction(
|
|
255
|
+
acc,
|
|
256
|
+
node => ($predicate(node) && $override) || undefined,
|
|
257
|
+
);
|
|
258
|
+
} else {
|
|
259
|
+
const typeOverrides = pair[1];
|
|
260
|
+
const compiled: Record<string, undefined | EditorDOMRenderConfig[K]> = {};
|
|
261
|
+
for (const type in typeOverrides) {
|
|
262
|
+
const arr = typeOverrides[type];
|
|
263
|
+
if (arr) {
|
|
264
|
+
compiled[type] = arr.reduce(
|
|
265
|
+
($acc, $override) => mergeFunction($acc, () => $override),
|
|
266
|
+
acc,
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
acc = mergeFunction(acc, node => {
|
|
271
|
+
const f = compiled[node.getType()];
|
|
272
|
+
return f && ignoreNextFunction(f);
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
defaults[k] = acc;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function addOverride<K extends keyof PreEditorDOMRenderConfig>(
|
|
280
|
+
prerender: PreEditorDOMRenderConfig,
|
|
281
|
+
k: K,
|
|
282
|
+
predicateOrTypes: PredicateOrTypes,
|
|
283
|
+
override: AnyDOMRenderMatch[K],
|
|
284
|
+
): void {
|
|
285
|
+
if (!override) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const arr = prerender[k];
|
|
289
|
+
if (typeof predicateOrTypes === 'function') {
|
|
290
|
+
arr.push([predicateOrTypes, override]);
|
|
291
|
+
} else {
|
|
292
|
+
const last = arr[arr.length - 1];
|
|
293
|
+
let types: TypeRender<AnyDOMRenderMatch[K]>;
|
|
294
|
+
if (last && last[0] === 'types') {
|
|
295
|
+
types = last[1];
|
|
296
|
+
} else {
|
|
297
|
+
types = {};
|
|
298
|
+
arr.push(['types', types]);
|
|
299
|
+
}
|
|
300
|
+
for (const type in predicateOrTypes) {
|
|
301
|
+
const typeArr = types[type] || [];
|
|
302
|
+
types[type] = typeArr;
|
|
303
|
+
typeArr.push(override);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
type NormalizedDOMRenderMatch<T> = Omit<
|
|
309
|
+
DOMRenderMatch<LexicalNode>,
|
|
310
|
+
'nodes'
|
|
311
|
+
> & {
|
|
312
|
+
nodes: T;
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
function isWildcard(
|
|
316
|
+
override: DOMRenderMatch<LexicalNode>,
|
|
317
|
+
): override is NormalizedDOMRenderMatch<'*'> {
|
|
318
|
+
return override.nodes === '*';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function sortedOverrides(
|
|
322
|
+
overrides: DOMRenderMatch<LexicalNode>[],
|
|
323
|
+
): DOMRenderMatch<LexicalNode>[] {
|
|
324
|
+
const byWildcard: NormalizedDOMRenderMatch<'*'>[] = [];
|
|
325
|
+
const byPredicate: NormalizedDOMRenderMatch<
|
|
326
|
+
[(node: LexicalNode) => node is LexicalNode]
|
|
327
|
+
>[] = [];
|
|
328
|
+
const byNode: NormalizedDOMRenderMatch<[Klass<LexicalNode>]>[] = [];
|
|
329
|
+
for (const override of overrides) {
|
|
330
|
+
if (isWildcard(override)) {
|
|
331
|
+
byWildcard.push(override);
|
|
332
|
+
} else if (Array.isArray(override.nodes)) {
|
|
333
|
+
for (const klassOrPredicate of override.nodes) {
|
|
334
|
+
if ($isLexicalNode(klassOrPredicate.prototype)) {
|
|
335
|
+
byNode.push(
|
|
336
|
+
override.nodes.length === 1
|
|
337
|
+
? (override as NormalizedDOMRenderMatch<[Klass<LexicalNode>]>)
|
|
338
|
+
: {...override, nodes: [klassOrPredicate]},
|
|
339
|
+
);
|
|
340
|
+
} else {
|
|
341
|
+
byPredicate.push(
|
|
342
|
+
override.nodes.length === 1
|
|
343
|
+
? (override as NormalizedDOMRenderMatch<
|
|
344
|
+
[(node: LexicalNode) => node is LexicalNode]
|
|
345
|
+
>)
|
|
346
|
+
: {...override, nodes: [klassOrPredicate]},
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const depths = new Map<Klass<LexicalNode>, number>();
|
|
353
|
+
const depthOf = (klass: Klass<LexicalNode>): number => {
|
|
354
|
+
let depth = depths.get(klass);
|
|
355
|
+
if (depth === undefined) {
|
|
356
|
+
depth = 0;
|
|
357
|
+
for (
|
|
358
|
+
let k: Klass<LexicalNode> = klass;
|
|
359
|
+
$isLexicalNode(k.prototype);
|
|
360
|
+
k = Object.getPrototypeOf(k)
|
|
361
|
+
) {
|
|
362
|
+
depth++;
|
|
363
|
+
}
|
|
364
|
+
depths.set(klass, depth);
|
|
365
|
+
}
|
|
366
|
+
return depth;
|
|
367
|
+
};
|
|
368
|
+
byNode.sort((a, b) => depthOf(a.nodes[0]) - depthOf(b.nodes[0]));
|
|
369
|
+
return [...byNode, ...byPredicate, ...byWildcard];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function precompileDOMRenderConfigOverrides(
|
|
373
|
+
editorConfig: Pick<InitialEditorConfig, 'nodes'>,
|
|
374
|
+
overrides: DOMRenderConfig['overrides'],
|
|
375
|
+
): PreEditorDOMRenderConfig {
|
|
376
|
+
const typeTree = buildTypeTree(editorConfig);
|
|
377
|
+
const prerender = makePrerender();
|
|
378
|
+
for (const override of sortedOverrides(overrides)) {
|
|
379
|
+
const predicateOrTypes = getPredicate(typeTree, override);
|
|
380
|
+
for (const k_ in prerender) {
|
|
381
|
+
const k = k_ as keyof typeof prerender;
|
|
382
|
+
addOverride(prerender, k, predicateOrTypes, override[k]);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
return prerender;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function identity<T>(v: T) {
|
|
389
|
+
return v;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export function compileDOMRenderConfigOverrides(
|
|
393
|
+
editorConfig: Pick<InitialEditorConfig, 'nodes' | 'dom'>,
|
|
394
|
+
{overrides}: Pick<DOMRenderConfig, 'overrides'>,
|
|
395
|
+
): EditorDOMRenderConfig {
|
|
396
|
+
const prerender = precompileDOMRenderConfigOverrides(editorConfig, overrides);
|
|
397
|
+
const dom = {
|
|
398
|
+
...DEFAULT_EDITOR_DOM_CONFIG,
|
|
399
|
+
...editorConfig.dom,
|
|
400
|
+
};
|
|
401
|
+
compilePrerenderKey(prerender, '$createDOM', dom, merge2, ignoreNext2);
|
|
402
|
+
compilePrerenderKey(prerender, '$exportDOM', dom, merge2, ignoreNext2);
|
|
403
|
+
compilePrerenderKey(prerender, '$extractWithChild', dom, merge5, ignoreNext5);
|
|
404
|
+
compilePrerenderKey(
|
|
405
|
+
prerender,
|
|
406
|
+
'$getDOMSlot',
|
|
407
|
+
dom,
|
|
408
|
+
merge3GetDOMSlot,
|
|
409
|
+
ignoreNext3GetDOMSlot,
|
|
410
|
+
);
|
|
411
|
+
compilePrerenderKey(prerender, '$shouldExclude', dom, merge3, ignoreNext3);
|
|
412
|
+
compilePrerenderKey(prerender, '$shouldInclude', dom, merge3, ignoreNext3);
|
|
413
|
+
compilePrerenderKey(prerender, '$updateDOM', dom, merge4, ignoreNext4);
|
|
414
|
+
compilePrerenderKey(prerender, '$decorateDOM', dom, sequence4, identity);
|
|
415
|
+
return dom;
|
|
416
|
+
}
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
export const DOMRenderExtensionName = '@lexical/html/DOM';
|
|
9
|
+
export const DOMRenderContextSymbol = Symbol.for(
|
|
10
|
+
'@lexical/html/DOMExportContext',
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export const DOMImportExtensionName = '@lexical/html/DOMImport';
|
|
14
|
+
export const DOMImportContextSymbol = Symbol.for(
|
|
15
|
+
'@lexical/html/DOMImportContext',
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
export const ALWAYS_TRUE = () => true as const;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
import type {
|
|
9
|
+
AnyDOMRenderMatch,
|
|
10
|
+
DOMOverrideOptions,
|
|
11
|
+
DOMRenderMatch,
|
|
12
|
+
DOMRenderMatchConfig,
|
|
13
|
+
NodeMatch,
|
|
14
|
+
} from './types';
|
|
15
|
+
import type {LexicalNode} from 'lexical';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A convenience function for type inference when constructing DOM overrides for
|
|
19
|
+
* use with {@link DOMRenderExtension}.
|
|
20
|
+
*
|
|
21
|
+
* The optional `options` argument controls *whether* the override is installed
|
|
22
|
+
* based only on render context — `disabledForEditor` gates residency in the
|
|
23
|
+
* editor's render pipeline (reconciliation), `disabledForSession` gates
|
|
24
|
+
* participation in a single export/generate session. See {@link DOMOverrideOptions}.
|
|
25
|
+
*
|
|
26
|
+
* @experimental
|
|
27
|
+
* @__NO_SIDE_EFFECTS__
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
export function domOverride(
|
|
31
|
+
nodes: '*',
|
|
32
|
+
config: DOMRenderMatchConfig<LexicalNode>,
|
|
33
|
+
options?: DOMOverrideOptions,
|
|
34
|
+
): DOMRenderMatch<LexicalNode>;
|
|
35
|
+
export function domOverride<T extends LexicalNode>(
|
|
36
|
+
nodes: readonly NodeMatch<T>[],
|
|
37
|
+
config: DOMRenderMatchConfig<T>,
|
|
38
|
+
options?: DOMOverrideOptions,
|
|
39
|
+
): DOMRenderMatch<T>;
|
|
40
|
+
export function domOverride(
|
|
41
|
+
nodes: AnyDOMRenderMatch['nodes'],
|
|
42
|
+
config: Omit<AnyDOMRenderMatch, 'nodes' | keyof DOMOverrideOptions>,
|
|
43
|
+
options?: DOMOverrideOptions,
|
|
44
|
+
): AnyDOMRenderMatch {
|
|
45
|
+
return {...config, ...options, nodes};
|
|
46
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
3
|
+
*
|
|
4
|
+
* This source code is licensed under the MIT license found in the
|
|
5
|
+
* LICENSE file in the root directory of this source tree.
|
|
6
|
+
*
|
|
7
|
+
*/
|
|
8
|
+
import {configExtension, defineExtension} from 'lexical';
|
|
9
|
+
|
|
10
|
+
import {CoreImportRules} from './coreImportRules';
|
|
11
|
+
import {DOMImportExtension} from './DOMImportExtension';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Bundles {@link CoreImportRules} into a {@link DOMImportExtension}-aware
|
|
15
|
+
* extension. Depend on this from your editor (directly or via richer
|
|
16
|
+
* extensions like `RichTextImportExtension`) to get the equivalent of the
|
|
17
|
+
* legacy core `importDOM` behavior for `<p>`, `<span>`, `<b>`,
|
|
18
|
+
* `<strong>`, `<em>`, `<i>`, `<code>`, `<mark>`, `<s>`, `<sub>`, `<sup>`,
|
|
19
|
+
* `<u>`, `<br>`, and `#text`.
|
|
20
|
+
*
|
|
21
|
+
* @experimental
|
|
22
|
+
*/
|
|
23
|
+
export const CoreImportExtension = defineExtension({
|
|
24
|
+
dependencies: [configExtension(DOMImportExtension, {rules: CoreImportRules})],
|
|
25
|
+
name: '@lexical/html/CoreImport',
|
|
26
|
+
});
|