@lexical/html 0.43.1-nightly.20260417.0 → 0.44.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.
@@ -9,8 +9,8 @@
9
9
  'use strict';
10
10
 
11
11
  var selection = require('@lexical/selection');
12
- var utils = require('@lexical/utils');
13
12
  var lexical = require('lexical');
13
+ var extension = require('@lexical/extension');
14
14
 
15
15
  /**
16
16
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -20,6 +20,565 @@ var lexical = require('lexical');
20
20
  *
21
21
  */
22
22
 
23
+ // Do not require this module directly! Use normal `invariant` calls.
24
+
25
+ function formatDevErrorMessage(message) {
26
+ throw new Error(message);
27
+ }
28
+
29
+ /**
30
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
31
+ *
32
+ * This source code is licensed under the MIT license found in the
33
+ * LICENSE file in the root directory of this source tree.
34
+ *
35
+ */
36
+
37
+ let activeContext;
38
+
39
+ /**
40
+ * @experimental
41
+ *
42
+ * The LexicalEditor with context
43
+ */
44
+
45
+ /**
46
+ * @experimental
47
+ *
48
+ * @param contextRecord The ContextRecord
49
+ * @param cfg The configuration
50
+ * @returns The value or defaultValue of cfg
51
+ */
52
+ function getContextValue(contextRecord, cfg) {
53
+ const {
54
+ key
55
+ } = cfg;
56
+ return contextRecord && key in contextRecord ? contextRecord[key] : cfg.defaultValue;
57
+ }
58
+ function getEditorContext(editor) {
59
+ return activeContext && activeContext.editor === editor ? activeContext : undefined;
60
+ }
61
+
62
+ /**
63
+ * @experimental
64
+ *
65
+ * @param sym The symbol for this ContextRecord (e.g. DOMRenderContextSymbol)
66
+ * @param editor The editor
67
+ * @returns The current context or undefined
68
+ */
69
+ function getContextRecord(sym, editor) {
70
+ const editorContext = getEditorContext(editor);
71
+ return editorContext && editorContext[sym];
72
+ }
73
+ function toPair(contextRecord, pairOrUpdater) {
74
+ if ('cfg' in pairOrUpdater) {
75
+ const {
76
+ cfg,
77
+ updater
78
+ } = pairOrUpdater;
79
+ return [cfg, updater(getContextValue(contextRecord, cfg))];
80
+ }
81
+ return pairOrUpdater;
82
+ }
83
+
84
+ /**
85
+ * Construct a new context from a parent context and pairs
86
+ *
87
+ * @param pairs The pairs and updaters to build the context from
88
+ * @param parent The parent context
89
+ * @returns The new context
90
+ */
91
+ function contextFromPairs(pairs, parent) {
92
+ let rval = parent;
93
+ for (const pairOrUpdater of pairs) {
94
+ const [k, v] = toPair(rval, pairOrUpdater);
95
+ const key = k.key;
96
+ if (rval === parent && getContextValue(rval, k) === v) {
97
+ continue;
98
+ }
99
+ const ctx = rval || createChildContext(parent);
100
+ ctx[key] = v;
101
+ rval = ctx;
102
+ }
103
+ return rval;
104
+ }
105
+ function createChildContext(parent) {
106
+ return Object.create(parent || null);
107
+ }
108
+
109
+ /**
110
+ * Create a context config pair that sets a value in the render context.
111
+ * @experimental
112
+ */
113
+ function contextValue(cfg, value) {
114
+ return [cfg, value];
115
+ }
116
+
117
+ /**
118
+ * Create a context config updater that transforms a value in the render context.
119
+ * @experimental
120
+ */
121
+ function contextUpdater(cfg, updater) {
122
+ return {
123
+ cfg,
124
+ updater
125
+ };
126
+ }
127
+
128
+ /**
129
+ * @internal
130
+ * @experimental
131
+ * @__NO_SIDE_EFFECTS__
132
+ */
133
+ function $withFullContext(sym, contextRecord, f, editor = lexical.$getEditor()) {
134
+ const prevDOMContext = activeContext;
135
+ const parentEditorContext = getEditorContext(editor);
136
+ try {
137
+ activeContext = {
138
+ ...parentEditorContext,
139
+ editor,
140
+ [sym]: contextRecord
141
+ };
142
+ return f();
143
+ } finally {
144
+ activeContext = prevDOMContext;
145
+ }
146
+ }
147
+
148
+ /**
149
+ * @internal
150
+ * @experimental
151
+ * @__NO_SIDE_EFFECTS__
152
+ */
153
+ function $withContext(sym, $defaults = () => undefined) {
154
+ return (cfg, editor = lexical.$getEditor()) => {
155
+ return f => {
156
+ const parentEditorContext = getEditorContext(editor);
157
+ const parentContextRecord = parentEditorContext && parentEditorContext[sym];
158
+ const contextRecord = contextFromPairs(cfg, parentContextRecord || $defaults(editor));
159
+ if (!contextRecord || contextRecord === parentContextRecord) {
160
+ return f();
161
+ }
162
+ return $withFullContext(sym, contextRecord, f, editor);
163
+ };
164
+ };
165
+ }
166
+
167
+ /**
168
+ * @experimental
169
+ * @internal
170
+ * @__NO_SIDE_EFFECTS__
171
+ */
172
+ function createContextState(tag, name, getDefaultValue, isEqual) {
173
+ return Object.assign(lexical.createState(Symbol(name), {
174
+ isEqual,
175
+ parse: getDefaultValue
176
+ }), {
177
+ [tag]: true
178
+ });
179
+ }
180
+
181
+ /**
182
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
183
+ *
184
+ * This source code is licensed under the MIT license found in the
185
+ * LICENSE file in the root directory of this source tree.
186
+ *
187
+ */
188
+ const DOMRenderExtensionName = '@lexical/html/DOM';
189
+ const DOMRenderContextSymbol = Symbol.for('@lexical/html/DOMExportContext');
190
+ const ALWAYS_TRUE = () => true;
191
+
192
+ function buildTypeTree(editorConfig) {
193
+ const t = {};
194
+ const {
195
+ nodes
196
+ } = extension.getKnownTypesAndNodes(editorConfig);
197
+ for (const klass of nodes) {
198
+ const type = klass.getType();
199
+ t[type] = {
200
+ klass,
201
+ types: {}
202
+ };
203
+ }
204
+ for (const baseRec of Object.values(t)) {
205
+ if (baseRec) {
206
+ const baseType = baseRec.klass.getType();
207
+ for (let {
208
+ klass
209
+ } = baseRec; lexical.$isLexicalNode(klass.prototype); klass = Object.getPrototypeOf(klass)) {
210
+ const {
211
+ ownNodeType
212
+ } = lexical.getStaticNodeConfig(klass);
213
+ const superRec = ownNodeType && t[ownNodeType];
214
+ if (superRec) {
215
+ superRec.types[baseType] = true;
216
+ }
217
+ }
218
+ }
219
+ }
220
+ return t;
221
+ }
222
+ function buildNodePredicate(klass) {
223
+ return node => node instanceof klass;
224
+ }
225
+ function getPredicate(typeTree, {
226
+ nodes
227
+ }) {
228
+ if (nodes === '*') {
229
+ return ALWAYS_TRUE;
230
+ }
231
+ let types = {};
232
+ const predicates = [];
233
+ for (const klassOrPredicate of nodes) {
234
+ if ('getType' in klassOrPredicate) {
235
+ const type = klassOrPredicate.getType();
236
+ if (types) {
237
+ const tree = typeTree[type];
238
+ if (!(tree !== undefined)) {
239
+ formatDevErrorMessage(`Node class ${klassOrPredicate.name} with type ${type} not registered in editor`);
240
+ }
241
+ types = Object.assign(types, tree.types);
242
+ }
243
+ predicates.push(buildNodePredicate(klassOrPredicate));
244
+ } else {
245
+ types = undefined;
246
+ predicates.push(klassOrPredicate);
247
+ }
248
+ }
249
+ if (types) {
250
+ return types;
251
+ } else if (predicates.length === 1) {
252
+ return predicates[0];
253
+ }
254
+ return node => {
255
+ for (const predicate of predicates) {
256
+ if (predicate(node)) {
257
+ return true;
258
+ }
259
+ }
260
+ return false;
261
+ };
262
+ }
263
+ function makePrerender() {
264
+ return {
265
+ $createDOM: [],
266
+ $decorateDOM: [],
267
+ $exportDOM: [],
268
+ $extractWithChild: [],
269
+ $getDOMSlot: [],
270
+ $shouldExclude: [],
271
+ $shouldInclude: [],
272
+ $updateDOM: []
273
+ };
274
+ }
275
+
276
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
277
+
278
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
279
+
280
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
281
+
282
+ function ignoreNext2(acc) {
283
+ return (node, _$next, editor) => acc(node, editor);
284
+ }
285
+ function ignoreNext3(acc) {
286
+ return (node, a, _$next, editor) => acc(node, a, editor);
287
+ }
288
+ function ignoreNext4(acc) {
289
+ return (node, a, b, _$next, editor) => acc(node, a, b, editor);
290
+ }
291
+ function ignoreNext5(acc) {
292
+ return (node, a, b, c, _$next, editor) => acc(node, a, b, c, editor);
293
+ }
294
+ function merge2($acc, $getOverride) {
295
+ return (node, editor) => {
296
+ const $next = () => $acc(node, editor);
297
+ const $override = $getOverride(node);
298
+ return $override ? $override(node, $next, editor) : $next();
299
+ };
300
+ }
301
+ function merge3(acc, $getOverride) {
302
+ return (node, a, editor) => {
303
+ const $next = () => acc(node, a, editor);
304
+ const $override = $getOverride(node);
305
+ return $override ? $override(node, a, $next, editor) : $next();
306
+ };
307
+ }
308
+ function merge4($acc, $getOverride) {
309
+ return (node, a, b, editor) => {
310
+ const $next = () => $acc(node, a, b, editor);
311
+ const $override = $getOverride(node);
312
+ return $override ? $override(node, a, b, $next, editor) : $next();
313
+ };
314
+ }
315
+ function merge5(acc, $getOverride) {
316
+ return (node, a, b, c, editor) => {
317
+ const $next = () => acc(node, a, b, c, editor);
318
+ const $override = $getOverride(node);
319
+ return $override ? $override(node, a, b, c, $next, editor) : $next();
320
+ };
321
+ }
322
+ function sequence4($acc, $getOverride) {
323
+ return (node, a, b, editor) => {
324
+ $acc(node, a, b, editor);
325
+ const $override = $getOverride(node);
326
+ if ($override) {
327
+ $override(node, a, b, editor);
328
+ }
329
+ };
330
+ }
331
+ function compilePrerenderKey(prerender, k, defaults, mergeFunction, ignoreNextFunction) {
332
+ let acc = defaults[k];
333
+ for (const pair of prerender[k]) {
334
+ if (typeof pair[0] === 'function') {
335
+ const [$predicate, $override] = pair;
336
+ acc = mergeFunction(acc, node => $predicate(node) && $override || undefined);
337
+ } else {
338
+ const typeOverrides = pair[1];
339
+ const compiled = {};
340
+ for (const type in typeOverrides) {
341
+ const arr = typeOverrides[type];
342
+ if (arr) {
343
+ compiled[type] = arr.reduce(($acc, $override) => mergeFunction($acc, () => $override), acc);
344
+ }
345
+ }
346
+ acc = mergeFunction(acc, node => {
347
+ const f = compiled[node.getType()];
348
+ return f && ignoreNextFunction(f);
349
+ });
350
+ }
351
+ }
352
+ defaults[k] = acc;
353
+ }
354
+ function addOverride(prerender, k, predicateOrTypes, override) {
355
+ if (!override) {
356
+ return;
357
+ }
358
+ const arr = prerender[k];
359
+ if (typeof predicateOrTypes === 'function') {
360
+ arr.push([predicateOrTypes, override]);
361
+ } else {
362
+ const last = arr[arr.length - 1];
363
+ let types;
364
+ if (last && last[0] === 'types') {
365
+ types = last[1];
366
+ } else {
367
+ types = {};
368
+ arr.push(['types', types]);
369
+ }
370
+ for (const type in predicateOrTypes) {
371
+ const typeArr = types[type] || [];
372
+ types[type] = typeArr;
373
+ typeArr.push(override);
374
+ }
375
+ }
376
+ }
377
+ function isWildcard(override) {
378
+ return override.nodes === '*';
379
+ }
380
+ function sortedOverrides(overrides) {
381
+ const byWildcard = [];
382
+ const byPredicate = [];
383
+ const byNode = [];
384
+ for (const override of overrides) {
385
+ if (isWildcard(override)) {
386
+ byWildcard.push(override);
387
+ } else if (Array.isArray(override.nodes)) {
388
+ for (const klassOrPredicate of override.nodes) {
389
+ if (lexical.$isLexicalNode(klassOrPredicate.prototype)) {
390
+ byNode.push(override.nodes.length === 1 ? override : {
391
+ ...override,
392
+ nodes: [klassOrPredicate]
393
+ });
394
+ } else {
395
+ byPredicate.push(override.nodes.length === 1 ? override : {
396
+ ...override,
397
+ nodes: [klassOrPredicate]
398
+ });
399
+ }
400
+ }
401
+ }
402
+ }
403
+ const depths = new Map();
404
+ const depthOf = klass => {
405
+ let depth = depths.get(klass);
406
+ if (depth === undefined) {
407
+ depth = 0;
408
+ for (let k = klass; lexical.$isLexicalNode(k.prototype); k = Object.getPrototypeOf(k)) {
409
+ depth++;
410
+ }
411
+ depths.set(klass, depth);
412
+ }
413
+ return depth;
414
+ };
415
+ byNode.sort((a, b) => depthOf(a.nodes[0]) - depthOf(b.nodes[0]));
416
+ return [...byNode, ...byPredicate, ...byWildcard];
417
+ }
418
+ function precompileDOMRenderConfigOverrides(editorConfig, overrides) {
419
+ const typeTree = buildTypeTree(editorConfig);
420
+ const prerender = makePrerender();
421
+ for (const override of sortedOverrides(overrides)) {
422
+ const predicateOrTypes = getPredicate(typeTree, override);
423
+ for (const k_ in prerender) {
424
+ const k = k_;
425
+ addOverride(prerender, k, predicateOrTypes, override[k]);
426
+ }
427
+ }
428
+ return prerender;
429
+ }
430
+ function identity(v) {
431
+ return v;
432
+ }
433
+ function compileDOMRenderConfigOverrides(editorConfig, {
434
+ overrides
435
+ }) {
436
+ const prerender = precompileDOMRenderConfigOverrides(editorConfig, overrides);
437
+ const dom = {
438
+ ...lexical.DEFAULT_EDITOR_DOM_CONFIG,
439
+ ...editorConfig.dom
440
+ };
441
+ compilePrerenderKey(prerender, '$createDOM', dom, merge2, ignoreNext2);
442
+ compilePrerenderKey(prerender, '$exportDOM', dom, merge2, ignoreNext2);
443
+ compilePrerenderKey(prerender, '$extractWithChild', dom, merge5, ignoreNext5);
444
+ compilePrerenderKey(prerender, '$getDOMSlot', dom, merge3, ignoreNext3);
445
+ compilePrerenderKey(prerender, '$shouldExclude', dom, merge3, ignoreNext3);
446
+ compilePrerenderKey(prerender, '$shouldInclude', dom, merge3, ignoreNext3);
447
+ compilePrerenderKey(prerender, '$updateDOM', dom, merge4, ignoreNext4);
448
+ compilePrerenderKey(prerender, '$decorateDOM', dom, sequence4, identity);
449
+ return dom;
450
+ }
451
+
452
+ /**
453
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
454
+ *
455
+ * This source code is licensed under the MIT license found in the
456
+ * LICENSE file in the root directory of this source tree.
457
+ *
458
+ */
459
+
460
+
461
+ /**
462
+ * @experimental
463
+ *
464
+ * An extension that allows overriding the render and export behavior for an
465
+ * editor. This is highly experimental and subject to change from one version
466
+ * to the next.
467
+ **/
468
+ const DOMRenderExtension = lexical.defineExtension({
469
+ build(editor, config, state) {
470
+ return {
471
+ defaults: contextFromPairs(config.contextDefaults, undefined)
472
+ };
473
+ },
474
+ config: {
475
+ contextDefaults: [],
476
+ overrides: []
477
+ },
478
+ html: {
479
+ // Define a RootNode export for $generateDOMFromRoot
480
+ export: new Map([[lexical.RootNode, () => {
481
+ const element = document.createElement('div');
482
+ element.role = 'textbox';
483
+ return {
484
+ element
485
+ };
486
+ }]])
487
+ },
488
+ init(editorConfig, config) {
489
+ editorConfig.dom = compileDOMRenderConfigOverrides(editorConfig, config);
490
+ },
491
+ mergeConfig(config, partial) {
492
+ const merged = lexical.shallowMergeConfig(config, partial);
493
+ for (const k of ['overrides', 'contextDefaults']) {
494
+ if (partial[k]) {
495
+ merged[k] = [...config[k], ...partial[k]];
496
+ }
497
+ }
498
+ return merged;
499
+ },
500
+ name: DOMRenderExtensionName
501
+ });
502
+
503
+ /**
504
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
505
+ *
506
+ * This source code is licensed under the MIT license found in the
507
+ * LICENSE file in the root directory of this source tree.
508
+ *
509
+ */
510
+ /**
511
+ * Create a context state to be used during render.
512
+ *
513
+ * Note that to support the ValueOrUpdater pattern you can not use a
514
+ * function for V (but you may wrap it in an array or object).
515
+ *
516
+ * @experimental
517
+ * @__NO_SIDE_EFFECTS__
518
+ */
519
+ function createRenderState(name, getDefaultValue, isEqual) {
520
+ return createContextState(DOMRenderContextSymbol, name, getDefaultValue, isEqual);
521
+ }
522
+
523
+ /**
524
+ * Render context state that is true if the export was initiated from the root of the document.
525
+ * @experimental
526
+ */
527
+ const RenderContextRoot = createRenderState('root', Boolean);
528
+
529
+ /**
530
+ * Render context state that is true if this is an export operation ($generateHtmlFromNodes).
531
+ * @experimental
532
+ */
533
+ const RenderContextExport = createRenderState('isExport', Boolean);
534
+ function getDefaultRenderContext(editor) {
535
+ const builder = extension.LexicalBuilder.maybeFromEditor(editor);
536
+ return builder && builder.hasExtensionByName(DOMRenderExtensionName) ? extension.getExtensionDependencyFromEditor(editor, DOMRenderExtension).output.defaults : undefined;
537
+ }
538
+ function getRenderContext(editor) {
539
+ return getContextRecord(DOMRenderContextSymbol, editor) || getDefaultRenderContext(editor);
540
+ }
541
+
542
+ /**
543
+ * Get a render context value during a DOM render or export operation.
544
+ * @experimental
545
+ */
546
+ function $getRenderContextValue(cfg, editor = lexical.$getEditor()) {
547
+ return getContextValue(getRenderContext(editor), cfg);
548
+ }
549
+
550
+ /**
551
+ * Execute a callback within a render context with the given config pairs.
552
+ * @experimental
553
+ */
554
+ const $withRenderContext = $withContext(DOMRenderContextSymbol, getDefaultRenderContext);
555
+
556
+ /**
557
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
558
+ *
559
+ * This source code is licensed under the MIT license found in the
560
+ * LICENSE file in the root directory of this source tree.
561
+ *
562
+ */
563
+
564
+ /**
565
+ * A convenience function for type inference when constructing DOM overrides for
566
+ * use with {@link DOMRenderExtension}.
567
+ *
568
+ * @experimental
569
+ * @__NO_SIDE_EFFECTS__
570
+ */
571
+
572
+ function domOverride(nodes, config) {
573
+ return {
574
+ ...config,
575
+ nodes
576
+ };
577
+ }
578
+
579
+ function isStyleRule(rule) {
580
+ return rule.constructor.name === CSSStyleRule.name;
581
+ }
23
582
 
24
583
  /**
25
584
  * Inlines CSS rules from <style> tags onto matching elements as inline styles.
@@ -56,7 +615,7 @@ function inlineStylesFromStyleSheets(doc) {
56
615
  continue;
57
616
  }
58
617
  for (const rule of Array.from(rules)) {
59
- if (!(rule instanceof CSSStyleRule)) {
618
+ if (!isStyleRule(rule)) {
60
619
  continue;
61
620
  }
62
621
  let elements;
@@ -66,7 +625,7 @@ function inlineStylesFromStyleSheets(doc) {
66
625
  continue;
67
626
  }
68
627
  for (const el of Array.from(elements)) {
69
- if (!(el instanceof HTMLElement)) {
628
+ if (!lexical.isHTMLElement(el)) {
70
629
  continue;
71
630
  }
72
631
  const originalProps = getOriginalInlineProps(el);
@@ -83,6 +642,7 @@ function inlineStylesFromStyleSheets(doc) {
83
642
  // styleSheets API not supported in this environment
84
643
  }
85
644
  }
645
+ const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']);
86
646
 
87
647
  /**
88
648
  * How you parse your html string to get a document is left up to you. In the browser you can use the native
@@ -94,69 +654,97 @@ function $generateNodesFromDOM(editor, dom) {
94
654
  inlineStylesFromStyleSheets(dom);
95
655
  }
96
656
  const elements = lexical.isDOMDocumentNode(dom) ? dom.body.childNodes : dom.childNodes;
97
- let lexicalNodes = [];
657
+ const lexicalNodes = [];
98
658
  const allArtificialNodes = [];
99
659
  for (const element of elements) {
100
660
  if (!IGNORE_TAGS.has(element.nodeName)) {
101
661
  const lexicalNode = $createNodesFromDOM(element, editor, allArtificialNodes, false);
102
662
  if (lexicalNode !== null) {
103
- lexicalNodes = lexicalNodes.concat(lexicalNode);
663
+ for (const node of lexicalNode) {
664
+ lexicalNodes.push(node);
665
+ }
104
666
  }
105
667
  }
106
668
  }
107
669
  $unwrapArtificialNodes(allArtificialNodes);
108
670
  return lexicalNodes;
109
671
  }
110
- function $generateHtmlFromNodes(editor, selection) {
672
+
673
+ /**
674
+ * Generate DOM nodes from the editor state into the given container element,
675
+ * using the editor's {@link EditorDOMRenderConfig}.
676
+ * @experimental
677
+ */
678
+ function $generateDOMFromNodes(container, selection = null, editor = lexical.$getEditor()) {
679
+ return $withRenderContext([contextValue(RenderContextExport, true)], editor)(() => {
680
+ const root = lexical.$getRoot();
681
+ const domConfig = lexical.$getEditorDOMRenderConfig(editor);
682
+ const parentElementAppend = container.append.bind(container);
683
+ for (const topLevelNode of root.getChildren()) {
684
+ $appendNodesToHTML(editor, topLevelNode, parentElementAppend, selection, domConfig);
685
+ }
686
+ return container;
687
+ });
688
+ }
689
+
690
+ /**
691
+ * Generate DOM nodes from a root node into the given container element,
692
+ * including the root node itself. Uses the editor's {@link EditorDOMRenderConfig}.
693
+ * @experimental
694
+ */
695
+ function $generateDOMFromRoot(container, root = lexical.$getRoot()) {
696
+ const editor = lexical.$getEditor();
697
+ return $withRenderContext([contextValue(RenderContextExport, true), contextValue(RenderContextRoot, true)], editor)(() => {
698
+ const selection = null;
699
+ const domConfig = lexical.$getEditorDOMRenderConfig(editor);
700
+ const parentElementAppend = container.append.bind(container);
701
+ $appendNodesToHTML(editor, root, parentElementAppend, selection, domConfig);
702
+ return container;
703
+ });
704
+ }
705
+ function $generateHtmlFromNodes(editor, selection = null) {
111
706
  if (typeof document === 'undefined' || typeof window === 'undefined' && typeof global.window === 'undefined') {
112
- throw new Error('To use $generateHtmlFromNodes in headless mode please initialize a headless browser implementation such as JSDom before calling this function.');
113
- }
114
- const container = document.createElement('div');
115
- const root = lexical.$getRoot();
116
- const topLevelChildren = root.getChildren();
117
- for (let i = 0; i < topLevelChildren.length; i++) {
118
- const topLevelNode = topLevelChildren[i];
119
- $appendNodesToHTML(editor, topLevelNode, container, selection);
707
+ {
708
+ formatDevErrorMessage(`To use $generateHtmlFromNodes in headless mode please initialize a headless browser implementation such as JSDom or use withDOM from @lexical/headless/dom before calling this function.`);
709
+ }
120
710
  }
121
- return container.innerHTML;
711
+ return $generateDOMFromNodes(document.createElement('div'), selection, editor).innerHTML;
122
712
  }
123
- function $appendNodesToHTML(editor, currentNode, parentElement, selection$1 = null) {
124
- let shouldInclude = selection$1 !== null ? currentNode.isSelected(selection$1) : true;
125
- const shouldExclude = lexical.$isElementNode(currentNode) && currentNode.excludeFromCopy('html');
713
+ function $appendNodesToHTML(editor, currentNode, parentElementAppend, selection$1 = null, domConfig = lexical.$getEditorDOMRenderConfig(editor)) {
714
+ let shouldInclude = domConfig.$shouldInclude(currentNode, selection$1, editor);
715
+ const shouldExclude = domConfig.$shouldExclude(currentNode, selection$1, editor);
126
716
  let target = currentNode;
127
717
  if (selection$1 !== null && lexical.$isTextNode(currentNode)) {
128
718
  target = selection.$sliceSelectedTextNodeContent(selection$1, currentNode, 'clone');
129
719
  }
130
- const children = lexical.$isElementNode(target) ? target.getChildren() : [];
131
- const registeredNode = lexical.getRegisteredNode(editor, target.getType());
132
- let exportOutput;
133
-
134
- // Use HTMLConfig overrides, if available.
135
- if (registeredNode && registeredNode.exportDOM !== undefined) {
136
- exportOutput = registeredNode.exportDOM(editor, target);
137
- } else {
138
- exportOutput = target.exportDOM(editor);
139
- }
720
+ const exportProps = domConfig.$exportDOM(target, editor);
140
721
  const {
141
722
  element,
142
- after
143
- } = exportOutput;
723
+ after,
724
+ append,
725
+ $getChildNodes
726
+ } = exportProps;
144
727
  if (!element) {
145
728
  return false;
146
729
  }
147
730
  const fragment = document.createDocumentFragment();
148
- for (let i = 0; i < children.length; i++) {
149
- const childNode = children[i];
150
- const shouldIncludeChild = $appendNodesToHTML(editor, childNode, fragment, selection$1);
151
- if (!shouldInclude && lexical.$isElementNode(currentNode) && shouldIncludeChild && currentNode.extractWithChild(childNode, selection$1, 'html')) {
731
+ const children = $getChildNodes ? $getChildNodes() : lexical.$isElementNode(target) ? target.getChildren() : [];
732
+ const fragmentAppend = fragment.append.bind(fragment);
733
+ for (const childNode of children) {
734
+ const shouldIncludeChild = $appendNodesToHTML(editor, childNode, fragmentAppend, selection$1, domConfig);
735
+ if (!shouldInclude && shouldIncludeChild && domConfig.$extractWithChild(currentNode, childNode, selection$1, 'html', editor)) {
152
736
  shouldInclude = true;
153
737
  }
154
738
  }
155
739
  if (shouldInclude && !shouldExclude) {
156
- if (utils.isHTMLElement(element) || lexical.isDocumentFragment(element)) {
157
- element.append(fragment);
740
+ if (lexical.isHTMLElement(element) || lexical.isDocumentFragment(element)) {
741
+ if (append) {
742
+ append(fragment);
743
+ } else {
744
+ element.append(fragment);
745
+ }
158
746
  }
159
- parentElement.append(element);
747
+ parentElementAppend(element);
160
748
  if (after) {
161
749
  const newElement = after.call(target, element);
162
750
  if (newElement) {
@@ -168,7 +756,7 @@ function $appendNodesToHTML(editor, currentNode, parentElement, selection$1 = nu
168
756
  }
169
757
  }
170
758
  } else {
171
- parentElement.append(fragment);
759
+ parentElementAppend(fragment);
172
760
  }
173
761
  return shouldInclude;
174
762
  }
@@ -191,9 +779,8 @@ function getConversionFunction(domNode, editor) {
191
779
  }
192
780
  return currentConversion !== null ? currentConversion.conversion : null;
193
781
  }
194
- const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']);
195
782
  function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorLexicalNode, forChildMap = new Map(), parentLexicalNode) {
196
- let lexicalNodes = [];
783
+ const lexicalNodes = [];
197
784
  if (IGNORE_TAGS.has(node.nodeName)) {
198
785
  return lexicalNodes;
199
786
  }
@@ -232,7 +819,7 @@ function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorL
232
819
  if (postTransform != null) {
233
820
  childLexicalNodes = postTransform(childLexicalNodes);
234
821
  }
235
- if (utils.isBlockDomNode(node)) {
822
+ if (lexical.isBlockDomNode(node)) {
236
823
  if (!hasBlockAncestorLexicalNodeForChildren) {
237
824
  childLexicalNodes = wrapContinuousInlines(node, childLexicalNodes, lexical.$createParagraphNode);
238
825
  } else {
@@ -247,11 +834,13 @@ function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorL
247
834
  if (childLexicalNodes.length > 0) {
248
835
  // If it hasn't been converted to a LexicalNode, we hoist its children
249
836
  // up to the same level as it.
250
- lexicalNodes = lexicalNodes.concat(childLexicalNodes);
837
+ for (const childNode of childLexicalNodes) {
838
+ lexicalNodes.push(childNode);
839
+ }
251
840
  } else {
252
- if (utils.isBlockDomNode(node) && isDomNodeBetweenTwoInlineNodes(node)) {
841
+ if (lexical.isBlockDomNode(node) && isDomNodeBetweenTwoInlineNodes(node)) {
253
842
  // Empty block dom node that hasnt been converted, we replace it with a linebreak if its between inline nodes
254
- lexicalNodes = lexicalNodes.concat(lexical.$createLineBreakNode());
843
+ lexicalNodes.push(lexical.$createLineBreakNode());
255
844
  }
256
845
  }
257
846
  } else {
@@ -289,18 +878,18 @@ function wrapContinuousInlines(domNode, nodes, createWrapperFn) {
289
878
  return out;
290
879
  }
291
880
  function $unwrapArtificialNodes(allArtificialNodes) {
881
+ // Replace artificial node with its children, inserting a linebreak
882
+ // between adjacent artificial nodes
292
883
  for (const node of allArtificialNodes) {
293
- if (node.getNextSibling() instanceof lexical.ArtificialNode__DO_NOT_USE) {
884
+ if (node.getParent() && node.getNextSibling() instanceof lexical.ArtificialNode__DO_NOT_USE) {
294
885
  node.insertAfter(lexical.$createLineBreakNode());
295
886
  }
296
887
  }
297
- // Replace artificial node with it's children
298
888
  for (const node of allArtificialNodes) {
299
- const children = node.getChildren();
300
- for (const child of children) {
301
- node.insertBefore(child);
889
+ const parent = node.getParent();
890
+ if (parent) {
891
+ parent.splice(node.getIndexWithinParent(), 1, node.getChildren());
302
892
  }
303
- node.remove();
304
893
  }
305
894
  }
306
895
  function isDomNodeBetweenTwoInlineNodes(node) {
@@ -310,5 +899,16 @@ function isDomNodeBetweenTwoInlineNodes(node) {
310
899
  return lexical.isInlineDomNode(node.nextSibling) && lexical.isInlineDomNode(node.previousSibling);
311
900
  }
312
901
 
902
+ exports.$generateDOMFromNodes = $generateDOMFromNodes;
903
+ exports.$generateDOMFromRoot = $generateDOMFromRoot;
313
904
  exports.$generateHtmlFromNodes = $generateHtmlFromNodes;
314
905
  exports.$generateNodesFromDOM = $generateNodesFromDOM;
906
+ exports.$getRenderContextValue = $getRenderContextValue;
907
+ exports.$withRenderContext = $withRenderContext;
908
+ exports.DOMRenderExtension = DOMRenderExtension;
909
+ exports.RenderContextExport = RenderContextExport;
910
+ exports.RenderContextRoot = RenderContextRoot;
911
+ exports.contextUpdater = contextUpdater;
912
+ exports.contextValue = contextValue;
913
+ exports.createRenderState = createRenderState;
914
+ exports.domOverride = domOverride;