@lexical/html 0.44.1-nightly.20260519.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.
Files changed (66) hide show
  1. package/{DOMRenderExtension.d.ts → dist/DOMRenderExtension.d.ts} +12 -1
  2. package/dist/DOMRenderRuntime.d.ts +51 -0
  3. package/dist/LexicalHtml.dev.js +3192 -0
  4. package/dist/LexicalHtml.dev.mjs +3146 -0
  5. package/{LexicalHtml.js.flow → dist/LexicalHtml.js.flow} +16 -16
  6. package/dist/LexicalHtml.mjs +56 -0
  7. package/dist/LexicalHtml.node.mjs +54 -0
  8. package/dist/LexicalHtml.prod.js +9 -0
  9. package/dist/LexicalHtml.prod.mjs +9 -0
  10. package/dist/RenderContext.d.ts +68 -0
  11. package/{compileDOMRenderConfigOverrides.d.ts → dist/compileDOMRenderConfigOverrides.d.ts} +1 -1
  12. package/{constants.d.ts → dist/constants.d.ts} +2 -0
  13. package/dist/domOverride.d.ts +23 -0
  14. package/dist/import/CoreImportExtension.d.ts +11 -0
  15. package/dist/import/DOMImportExtension.d.ts +82 -0
  16. package/dist/import/HorizontalRuleImportExtension.d.ts +27 -0
  17. package/dist/import/ImportContext.d.ts +208 -0
  18. package/dist/import/compileImportRules.d.ts +50 -0
  19. package/dist/import/coreImportRules.d.ts +25 -0
  20. package/dist/import/defineImportRule.d.ts +32 -0
  21. package/dist/import/defineOverlayRules.d.ts +66 -0
  22. package/dist/import/index.d.ts +38 -0
  23. package/dist/import/inlineStylesFromStyleSheets.d.ts +28 -0
  24. package/dist/import/parseCss.d.ts +18 -0
  25. package/dist/import/runImport.d.ts +19 -0
  26. package/dist/import/schemas.d.ts +91 -0
  27. package/dist/import/sel.d.ts +74 -0
  28. package/dist/import/types.d.ts +394 -0
  29. package/dist/index.d.ts +44 -0
  30. package/{types.d.ts → dist/types.d.ts} +96 -8
  31. package/package.json +33 -18
  32. package/src/ContextRecord.ts +243 -0
  33. package/src/DOMRenderExtension.ts +96 -0
  34. package/src/DOMRenderRuntime.ts +265 -0
  35. package/src/RenderContext.ts +168 -0
  36. package/src/compileDOMRenderConfigOverrides.ts +416 -0
  37. package/src/constants.ts +18 -0
  38. package/src/domOverride.ts +46 -0
  39. package/src/import/CoreImportExtension.ts +26 -0
  40. package/src/import/DOMImportExtension.ts +221 -0
  41. package/src/import/HorizontalRuleImportExtension.ts +53 -0
  42. package/src/import/ImportContext.ts +339 -0
  43. package/src/import/compileImportRules.ts +178 -0
  44. package/src/import/coreImportRules.ts +485 -0
  45. package/src/import/defineImportRule.ts +40 -0
  46. package/src/import/defineOverlayRules.ts +105 -0
  47. package/src/import/index.ts +96 -0
  48. package/src/import/inlineStylesFromStyleSheets.ts +104 -0
  49. package/src/import/parseCss.ts +219 -0
  50. package/src/import/runImport.ts +245 -0
  51. package/src/import/schemas.ts +236 -0
  52. package/src/import/sel.ts +314 -0
  53. package/src/import/types.ts +471 -0
  54. package/src/index.ts +555 -0
  55. package/src/types.ts +470 -0
  56. package/LexicalHtml.dev.js +0 -914
  57. package/LexicalHtml.dev.mjs +0 -900
  58. package/LexicalHtml.mjs +0 -24
  59. package/LexicalHtml.node.mjs +0 -22
  60. package/LexicalHtml.prod.js +0 -9
  61. package/LexicalHtml.prod.mjs +0 -9
  62. package/RenderContext.d.ts +0 -32
  63. package/domOverride.d.ts +0 -18
  64. package/index.d.ts +0 -32
  65. /package/{ContextRecord.d.ts → dist/ContextRecord.d.ts} +0 -0
  66. /package/{LexicalHtml.js → dist/LexicalHtml.js} +0 -0
@@ -0,0 +1,3192 @@
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
+
9
+ 'use strict';
10
+
11
+ var selection = require('@lexical/selection');
12
+ var lexical = require('lexical');
13
+ var utils = require('@lexical/utils');
14
+ var extension = require('@lexical/extension');
15
+
16
+ /**
17
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
18
+ *
19
+ * This source code is licensed under the MIT license found in the
20
+ * LICENSE file in the root directory of this source tree.
21
+ *
22
+ */
23
+
24
+ // Do not require this module directly! Use normal `invariant` calls.
25
+
26
+ function formatDevErrorMessage(message) {
27
+ throw new Error(message);
28
+ }
29
+
30
+ /**
31
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
32
+ *
33
+ * This source code is licensed under the MIT license found in the
34
+ * LICENSE file in the root directory of this source tree.
35
+ *
36
+ */
37
+
38
+ let activeContext;
39
+
40
+ /**
41
+ * @experimental
42
+ *
43
+ * The LexicalEditor with context
44
+ */
45
+
46
+ /**
47
+ * @experimental
48
+ *
49
+ * @param contextRecord The ContextRecord
50
+ * @param cfg The configuration
51
+ * @returns The value or defaultValue of cfg
52
+ */
53
+ function getContextValue(contextRecord, cfg) {
54
+ const {
55
+ key
56
+ } = cfg;
57
+ return contextRecord && key in contextRecord ? contextRecord[key] : cfg.defaultValue;
58
+ }
59
+ function getEditorContext(editor) {
60
+ return activeContext && activeContext.editor === editor ? activeContext : undefined;
61
+ }
62
+
63
+ /**
64
+ * @experimental
65
+ *
66
+ * @param sym The symbol for this ContextRecord (e.g. DOMRenderContextSymbol)
67
+ * @param editor The editor
68
+ * @returns The current context or undefined
69
+ */
70
+ function getContextRecord(sym, editor) {
71
+ const editorContext = getEditorContext(editor);
72
+ return editorContext && editorContext[sym];
73
+ }
74
+ function toPair(contextRecord, pairOrUpdater) {
75
+ if ('cfg' in pairOrUpdater) {
76
+ const {
77
+ cfg,
78
+ updater
79
+ } = pairOrUpdater;
80
+ return [cfg, updater(getContextValue(contextRecord, cfg))];
81
+ }
82
+ return pairOrUpdater;
83
+ }
84
+
85
+ /**
86
+ * Construct a new context from a parent context and pairs
87
+ *
88
+ * @param pairs The pairs and updaters to build the context from
89
+ * @param parent The parent context
90
+ * @returns The new context
91
+ */
92
+ function contextFromPairs(pairs, parent) {
93
+ let rval = parent;
94
+ for (const pairOrUpdater of pairs) {
95
+ const [k, v] = toPair(rval, pairOrUpdater);
96
+ const key = k.key;
97
+ if (rval === parent && getContextValue(rval, k) === v) {
98
+ continue;
99
+ }
100
+ // If we haven't branched away from `parent` yet, create a fresh child
101
+ // context so we never mutate the caller's parent record. Subsequent
102
+ // pairs in this loop accumulate into the same child. Inside the loop
103
+ // `rval` is non-null after the first iteration, since createChildContext
104
+ // never returns null/undefined.
105
+ const ctx = rval === parent || rval === undefined ? createChildContext(parent) : rval;
106
+ ctx[key] = v;
107
+ rval = ctx;
108
+ }
109
+ return rval;
110
+ }
111
+ function createChildContext(parent) {
112
+ return Object.create(parent || null);
113
+ }
114
+
115
+ /**
116
+ * Create a context config pair that sets a value in the render context.
117
+ * @experimental
118
+ */
119
+ function contextValue(cfg, value) {
120
+ return [cfg, value];
121
+ }
122
+
123
+ /**
124
+ * Create a context config updater that transforms a value in the render context.
125
+ * @experimental
126
+ */
127
+ function contextUpdater(cfg, updater) {
128
+ return {
129
+ cfg,
130
+ updater
131
+ };
132
+ }
133
+
134
+ /**
135
+ * @internal
136
+ * @experimental
137
+ * @__NO_SIDE_EFFECTS__
138
+ */
139
+ function $withFullContext(sym, contextRecord, f, editor = lexical.$getEditor()) {
140
+ const prevDOMContext = activeContext;
141
+ const parentEditorContext = getEditorContext(editor);
142
+ try {
143
+ activeContext = {
144
+ ...parentEditorContext,
145
+ editor,
146
+ [sym]: contextRecord
147
+ };
148
+ return f();
149
+ } finally {
150
+ activeContext = prevDOMContext;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * @internal
156
+ * @experimental
157
+ * @__NO_SIDE_EFFECTS__
158
+ */
159
+ function $withContext(sym, $defaults = () => undefined) {
160
+ return (cfg, editor = lexical.$getEditor()) => {
161
+ return f => {
162
+ const parentEditorContext = getEditorContext(editor);
163
+ const parentContextRecord = parentEditorContext && parentEditorContext[sym];
164
+ const contextRecord = contextFromPairs(cfg, parentContextRecord || $defaults(editor));
165
+ if (!contextRecord || contextRecord === parentContextRecord) {
166
+ return f();
167
+ }
168
+ return $withFullContext(sym, contextRecord, f, editor);
169
+ };
170
+ };
171
+ }
172
+
173
+ /**
174
+ * @experimental
175
+ * @internal
176
+ * @__NO_SIDE_EFFECTS__
177
+ */
178
+ function createContextState(tag, name, getDefaultValue, isEqual) {
179
+ return Object.assign(lexical.createState(Symbol(name), {
180
+ isEqual,
181
+ parse: getDefaultValue
182
+ }), {
183
+ [tag]: true
184
+ });
185
+ }
186
+
187
+ /**
188
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
189
+ *
190
+ * This source code is licensed under the MIT license found in the
191
+ * LICENSE file in the root directory of this source tree.
192
+ *
193
+ */
194
+
195
+
196
+ /**
197
+ * Inlines CSS rules from `<style>` tags onto matching elements as inline
198
+ * styles.
199
+ *
200
+ * Used by apps like Excel that generate HTML where styles live in
201
+ * class-based `<style>` rules (e.g. `.xl65 { background: #FFFF00; color:
202
+ * blue; }`) rather than inline styles. Since Lexical's import converters
203
+ * read inline styles, we resolve stylesheet rules into inline styles
204
+ * before conversion.
205
+ *
206
+ * Mutates the DOM in-place. Original inline styles always take
207
+ * precedence over stylesheet rules (matching CSS specificity behavior).
208
+ *
209
+ * No-op for {@link ParentNode}s that are not {@link Document}s — only a
210
+ * full document carries `styleSheets` we can iterate.
211
+ *
212
+ * @experimental
213
+ */
214
+ const $inlineStylesFromStyleSheets = (dom, _ctx, $next) => {
215
+ $inlineStylesFromStyleSheetsDOM(dom);
216
+ $next();
217
+ };
218
+ function $inlineStylesFromStyleSheetsDOM(dom) {
219
+ if (!lexical.isDOMDocumentNode(dom)) {
220
+ return;
221
+ }
222
+ const doc = dom;
223
+ if (doc.querySelector('style') === null) {
224
+ return;
225
+ }
226
+ const originalInlineStyles = new Map();
227
+ function getOriginalInlineProps(el) {
228
+ let props = originalInlineStyles.get(el);
229
+ if (props === undefined) {
230
+ props = new Set();
231
+ for (let i = 0; i < el.style.length; i++) {
232
+ props.add(el.style[i]);
233
+ }
234
+ originalInlineStyles.set(el, props);
235
+ }
236
+ return props;
237
+ }
238
+ try {
239
+ for (const sheet of Array.from(doc.styleSheets)) {
240
+ let rules;
241
+ try {
242
+ rules = sheet.cssRules;
243
+ } catch (_unused) {
244
+ continue;
245
+ }
246
+ for (const rule of Array.from(rules)) {
247
+ if (!utils.objectKlassEquals(rule, CSSStyleRule)) {
248
+ continue;
249
+ }
250
+ let elements;
251
+ try {
252
+ elements = doc.querySelectorAll(rule.selectorText);
253
+ } catch (_unused2) {
254
+ continue;
255
+ }
256
+ for (const el of Array.from(elements)) {
257
+ if (!lexical.isHTMLElement(el)) {
258
+ continue;
259
+ }
260
+ const originalProps = getOriginalInlineProps(el);
261
+ for (let i = 0; i < rule.style.length; i++) {
262
+ const prop = rule.style[i];
263
+ if (!originalProps.has(prop)) {
264
+ el.style.setProperty(prop, rule.style.getPropertyValue(prop), rule.style.getPropertyPriority(prop));
265
+ }
266
+ }
267
+ }
268
+ }
269
+ }
270
+ } catch (_unused3) {
271
+ // styleSheets API not supported in this environment
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
277
+ *
278
+ * This source code is licensed under the MIT license found in the
279
+ * LICENSE file in the root directory of this source tree.
280
+ *
281
+ */
282
+ const DOMRenderExtensionName = '@lexical/html/DOM';
283
+ const DOMRenderContextSymbol = Symbol.for('@lexical/html/DOMExportContext');
284
+ const DOMImportExtensionName = '@lexical/html/DOMImport';
285
+ const DOMImportContextSymbol = Symbol.for('@lexical/html/DOMImportContext');
286
+ const ALWAYS_TRUE = () => true;
287
+
288
+ /**
289
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
290
+ *
291
+ * This source code is licensed under the MIT license found in the
292
+ * LICENSE file in the root directory of this source tree.
293
+ *
294
+ */
295
+
296
+ /**
297
+ * Create a context state to be used during render.
298
+ *
299
+ * Note that to support the ValueOrUpdater pattern you can not use a
300
+ * function for V (but you may wrap it in an array or object).
301
+ *
302
+ * @experimental
303
+ * @__NO_SIDE_EFFECTS__
304
+ */
305
+ function createRenderState(name, getDefaultValue, isEqual) {
306
+ return createContextState(DOMRenderContextSymbol, name, getDefaultValue, isEqual);
307
+ }
308
+
309
+ /**
310
+ * Render context state that is true if the export was initiated from the root of the document.
311
+ * @experimental
312
+ */
313
+ const RenderContextRoot = createRenderState('root', Boolean);
314
+
315
+ /**
316
+ * Render context state that is true if this is an export operation ($generateHtmlFromNodes).
317
+ * @experimental
318
+ */
319
+ const RenderContextExport = createRenderState('isExport', Boolean);
320
+ function getDefaultRenderContext(editor) {
321
+ const dep = extension.getPeerDependencyFromEditor(editor, DOMRenderExtensionName);
322
+ return dep ? dep.output.defaults : undefined;
323
+ }
324
+ function getRenderContext(editor) {
325
+ return getContextRecord(DOMRenderContextSymbol, editor) || getDefaultRenderContext(editor);
326
+ }
327
+
328
+ /**
329
+ * Get a render context value during a DOM render or export operation.
330
+ * @experimental
331
+ */
332
+ function $getRenderContextValue(cfg, editor = lexical.$getEditor()) {
333
+ return getContextValue(getRenderContext(editor), cfg);
334
+ }
335
+ function getRuntime(editor) {
336
+ const dep = extension.getPeerDependencyFromEditor(editor, DOMRenderExtensionName);
337
+ return dep ? dep.output.runtime : undefined;
338
+ }
339
+
340
+ /**
341
+ * Imperatively set a value in the persistent editor render context.
342
+ *
343
+ * Unlike {@link $withRenderContext} (which scopes values to a callback), this
344
+ * persists on the editor. If the change flips any override's
345
+ * `disabledForEditor` result, the resident render config is recompiled and the
346
+ * affected nodes are re-rendered. No-op if {@link DOMRenderExtension} is not
347
+ * installed.
348
+ *
349
+ * @experimental
350
+ */
351
+ function $setRenderContextValue(cfg, value, editor = lexical.$getEditor()) {
352
+ const runtime = getRuntime(editor);
353
+ if (runtime) {
354
+ runtime.setContextValue(cfg, value);
355
+ }
356
+ }
357
+
358
+ /**
359
+ * Imperatively update a value in the persistent editor render context with an
360
+ * updater function. See {@link $setRenderContextValue}.
361
+ *
362
+ * @experimental
363
+ */
364
+ function $updateRenderContextValue(cfg, updater, editor = lexical.$getEditor()) {
365
+ const runtime = getRuntime(editor);
366
+ if (runtime) {
367
+ runtime.setContextValue(cfg, updater(getContextValue(runtime.editorContext, cfg)));
368
+ }
369
+ }
370
+
371
+ /**
372
+ * Resolve the {@link EditorDOMRenderConfig} to use for the current
373
+ * export/generate session, applying any `disabledForSession` overrides against
374
+ * the active session context. Falls back to the editor's resident config when
375
+ * {@link DOMRenderExtension} is not installed.
376
+ *
377
+ * @experimental
378
+ */
379
+ function $getSessionDOMRenderConfig(editor = lexical.$getEditor()) {
380
+ const runtime = getRuntime(editor);
381
+ return runtime ? runtime.getSessionConfig() : lexical.$getEditorDOMRenderConfig(editor);
382
+ }
383
+
384
+ /**
385
+ * Execute a callback within a render context with the given config pairs.
386
+ * @experimental
387
+ */
388
+ const $withRenderContext = $withContext(DOMRenderContextSymbol, getDefaultRenderContext);
389
+
390
+ /**
391
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
392
+ *
393
+ * This source code is licensed under the MIT license found in the
394
+ * LICENSE file in the root directory of this source tree.
395
+ *
396
+ */
397
+
398
+ /**
399
+ * A convenience function for type inference when constructing DOM overrides for
400
+ * use with {@link DOMRenderExtension}.
401
+ *
402
+ * The optional `options` argument controls *whether* the override is installed
403
+ * based only on render context — `disabledForEditor` gates residency in the
404
+ * editor's render pipeline (reconciliation), `disabledForSession` gates
405
+ * participation in a single export/generate session. See {@link DOMOverrideOptions}.
406
+ *
407
+ * @experimental
408
+ * @__NO_SIDE_EFFECTS__
409
+ */
410
+
411
+ function domOverride(nodes, config, options) {
412
+ return {
413
+ ...config,
414
+ ...options,
415
+ nodes
416
+ };
417
+ }
418
+
419
+ function buildTypeTree(editorConfig) {
420
+ const t = {};
421
+ const {
422
+ nodes
423
+ } = extension.getKnownTypesAndNodes(editorConfig);
424
+ for (const klass of nodes) {
425
+ const type = klass.getType();
426
+ t[type] = {
427
+ klass,
428
+ types: {}
429
+ };
430
+ }
431
+ for (const baseRec of Object.values(t)) {
432
+ if (baseRec) {
433
+ const baseType = baseRec.klass.getType();
434
+ for (let {
435
+ klass
436
+ } = baseRec; lexical.$isLexicalNode(klass.prototype); klass = Object.getPrototypeOf(klass)) {
437
+ const {
438
+ ownNodeType
439
+ } = lexical.getStaticNodeConfig(klass);
440
+ const superRec = ownNodeType && t[ownNodeType];
441
+ if (superRec) {
442
+ superRec.types[baseType] = true;
443
+ }
444
+ }
445
+ }
446
+ }
447
+ return t;
448
+ }
449
+ function buildNodePredicate(klass) {
450
+ return node => node instanceof klass;
451
+ }
452
+ function getPredicate(typeTree, {
453
+ nodes
454
+ }) {
455
+ if (nodes === '*') {
456
+ return ALWAYS_TRUE;
457
+ }
458
+ let types = {};
459
+ const predicates = [];
460
+ for (const klassOrPredicate of nodes) {
461
+ if ('getType' in klassOrPredicate) {
462
+ const type = klassOrPredicate.getType();
463
+ if (types) {
464
+ const tree = typeTree[type];
465
+ if (!(tree !== undefined)) {
466
+ formatDevErrorMessage(`Node class ${klassOrPredicate.name} with type ${type} not registered in editor`);
467
+ }
468
+ types = Object.assign(types, tree.types);
469
+ }
470
+ predicates.push(buildNodePredicate(klassOrPredicate));
471
+ } else {
472
+ types = undefined;
473
+ predicates.push(klassOrPredicate);
474
+ }
475
+ }
476
+ if (types) {
477
+ return types;
478
+ } else if (predicates.length === 1) {
479
+ return predicates[0];
480
+ }
481
+ return node => {
482
+ for (const predicate of predicates) {
483
+ if (predicate(node)) {
484
+ return true;
485
+ }
486
+ }
487
+ return false;
488
+ };
489
+ }
490
+ function makePrerender() {
491
+ return {
492
+ $createDOM: [],
493
+ $decorateDOM: [],
494
+ $exportDOM: [],
495
+ $extractWithChild: [],
496
+ $getDOMSlot: [],
497
+ $shouldExclude: [],
498
+ $shouldInclude: [],
499
+ $updateDOM: []
500
+ };
501
+ }
502
+
503
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
504
+
505
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
506
+
507
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
508
+
509
+ function ignoreNext2(acc) {
510
+ return (node, _$next, editor) => acc(node, editor);
511
+ }
512
+ function ignoreNext3(acc) {
513
+ return (node, a, _$next, editor) => acc(node, a, editor);
514
+ }
515
+ function ignoreNext4(acc) {
516
+ return (node, a, b, _$next, editor) => acc(node, a, b, editor);
517
+ }
518
+ function ignoreNext5(acc) {
519
+ return (node, a, b, c, _$next, editor) => acc(node, a, b, c, editor);
520
+ }
521
+ function merge2($acc, $getOverride) {
522
+ return (node, editor) => {
523
+ const $next = () => $acc(node, editor);
524
+ const $override = $getOverride(node);
525
+ return $override ? $override(node, $next, editor) : $next();
526
+ };
527
+ }
528
+ function merge3(acc, $getOverride) {
529
+ return (node, a, editor) => {
530
+ const $next = () => acc(node, a, editor);
531
+ const $override = $getOverride(node);
532
+ return $override ? $override(node, a, $next, editor) : $next();
533
+ };
534
+ }
535
+ const merge3GetDOMSlot = merge3;
536
+ const ignoreNext3GetDOMSlot = ignoreNext3;
537
+ function merge4($acc, $getOverride) {
538
+ return (node, a, b, editor) => {
539
+ const $next = () => $acc(node, a, b, editor);
540
+ const $override = $getOverride(node);
541
+ return $override ? $override(node, a, b, $next, editor) : $next();
542
+ };
543
+ }
544
+ function merge5(acc, $getOverride) {
545
+ return (node, a, b, c, editor) => {
546
+ const $next = () => acc(node, a, b, c, editor);
547
+ const $override = $getOverride(node);
548
+ return $override ? $override(node, a, b, c, $next, editor) : $next();
549
+ };
550
+ }
551
+ function sequence4($acc, $getOverride) {
552
+ return (node, a, b, editor) => {
553
+ $acc(node, a, b, editor);
554
+ const $override = $getOverride(node);
555
+ if ($override) {
556
+ $override(node, a, b, editor);
557
+ }
558
+ };
559
+ }
560
+ function compilePrerenderKey(prerender, k, defaults, mergeFunction, ignoreNextFunction) {
561
+ let acc = defaults[k];
562
+ for (const pair of prerender[k]) {
563
+ if (typeof pair[0] === 'function') {
564
+ const [$predicate, $override] = pair;
565
+ acc = mergeFunction(acc, node => $predicate(node) && $override || undefined);
566
+ } else {
567
+ const typeOverrides = pair[1];
568
+ const compiled = {};
569
+ for (const type in typeOverrides) {
570
+ const arr = typeOverrides[type];
571
+ if (arr) {
572
+ compiled[type] = arr.reduce(($acc, $override) => mergeFunction($acc, () => $override), acc);
573
+ }
574
+ }
575
+ acc = mergeFunction(acc, node => {
576
+ const f = compiled[node.getType()];
577
+ return f && ignoreNextFunction(f);
578
+ });
579
+ }
580
+ }
581
+ defaults[k] = acc;
582
+ }
583
+ function addOverride(prerender, k, predicateOrTypes, override) {
584
+ if (!override) {
585
+ return;
586
+ }
587
+ const arr = prerender[k];
588
+ if (typeof predicateOrTypes === 'function') {
589
+ arr.push([predicateOrTypes, override]);
590
+ } else {
591
+ const last = arr[arr.length - 1];
592
+ let types;
593
+ if (last && last[0] === 'types') {
594
+ types = last[1];
595
+ } else {
596
+ types = {};
597
+ arr.push(['types', types]);
598
+ }
599
+ for (const type in predicateOrTypes) {
600
+ const typeArr = types[type] || [];
601
+ types[type] = typeArr;
602
+ typeArr.push(override);
603
+ }
604
+ }
605
+ }
606
+ function isWildcard(override) {
607
+ return override.nodes === '*';
608
+ }
609
+ function sortedOverrides(overrides) {
610
+ const byWildcard = [];
611
+ const byPredicate = [];
612
+ const byNode = [];
613
+ for (const override of overrides) {
614
+ if (isWildcard(override)) {
615
+ byWildcard.push(override);
616
+ } else if (Array.isArray(override.nodes)) {
617
+ for (const klassOrPredicate of override.nodes) {
618
+ if (lexical.$isLexicalNode(klassOrPredicate.prototype)) {
619
+ byNode.push(override.nodes.length === 1 ? override : {
620
+ ...override,
621
+ nodes: [klassOrPredicate]
622
+ });
623
+ } else {
624
+ byPredicate.push(override.nodes.length === 1 ? override : {
625
+ ...override,
626
+ nodes: [klassOrPredicate]
627
+ });
628
+ }
629
+ }
630
+ }
631
+ }
632
+ const depths = new Map();
633
+ const depthOf = klass => {
634
+ let depth = depths.get(klass);
635
+ if (depth === undefined) {
636
+ depth = 0;
637
+ for (let k = klass; lexical.$isLexicalNode(k.prototype); k = Object.getPrototypeOf(k)) {
638
+ depth++;
639
+ }
640
+ depths.set(klass, depth);
641
+ }
642
+ return depth;
643
+ };
644
+ byNode.sort((a, b) => depthOf(a.nodes[0]) - depthOf(b.nodes[0]));
645
+ return [...byNode, ...byPredicate, ...byWildcard];
646
+ }
647
+ function precompileDOMRenderConfigOverrides(editorConfig, overrides) {
648
+ const typeTree = buildTypeTree(editorConfig);
649
+ const prerender = makePrerender();
650
+ for (const override of sortedOverrides(overrides)) {
651
+ const predicateOrTypes = getPredicate(typeTree, override);
652
+ for (const k_ in prerender) {
653
+ const k = k_;
654
+ addOverride(prerender, k, predicateOrTypes, override[k]);
655
+ }
656
+ }
657
+ return prerender;
658
+ }
659
+ function identity(v) {
660
+ return v;
661
+ }
662
+ function compileDOMRenderConfigOverrides(editorConfig, {
663
+ overrides
664
+ }) {
665
+ const prerender = precompileDOMRenderConfigOverrides(editorConfig, overrides);
666
+ const dom = {
667
+ ...lexical.DEFAULT_EDITOR_DOM_CONFIG,
668
+ ...editorConfig.dom
669
+ };
670
+ compilePrerenderKey(prerender, '$createDOM', dom, merge2, ignoreNext2);
671
+ compilePrerenderKey(prerender, '$exportDOM', dom, merge2, ignoreNext2);
672
+ compilePrerenderKey(prerender, '$extractWithChild', dom, merge5, ignoreNext5);
673
+ compilePrerenderKey(prerender, '$getDOMSlot', dom, merge3GetDOMSlot, ignoreNext3GetDOMSlot);
674
+ compilePrerenderKey(prerender, '$shouldExclude', dom, merge3, ignoreNext3);
675
+ compilePrerenderKey(prerender, '$shouldInclude', dom, merge3, ignoreNext3);
676
+ compilePrerenderKey(prerender, '$updateDOM', dom, merge4, ignoreNext4);
677
+ compilePrerenderKey(prerender, '$decorateDOM', dom, sequence4, identity);
678
+ return dom;
679
+ }
680
+
681
+ /**
682
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
683
+ *
684
+ * This source code is licensed under the MIT license found in the
685
+ * LICENSE file in the root directory of this source tree.
686
+ *
687
+ */
688
+
689
+ function makeReader(record) {
690
+ return {
691
+ get(cfg) {
692
+ return getContextValue(record, cfg);
693
+ }
694
+ };
695
+ }
696
+
697
+ /**
698
+ * The mutable, writable editor-level context record. Reads of a render state
699
+ * during reconciliation (and as the base layer of a session) fall through to
700
+ * this record, and it is the layer the `disabledForEditor` predicates read.
701
+ *
702
+ * @internal
703
+ */
704
+ function createEditorContextRecord(contextDefaults) {
705
+ const parent = Object.create(null);
706
+ return contextFromPairs(contextDefaults, parent) || parent;
707
+ }
708
+
709
+ /**
710
+ * Filter the configured overrides down to those that are resident in the
711
+ * editor's render config, removing any whose `disabledForEditor` predicate
712
+ * returns `true` for the given editor context.
713
+ *
714
+ * @internal
715
+ */
716
+ function filterEditorInstalled(overrides, record) {
717
+ const reader = makeReader(record);
718
+ return overrides.filter(o => !(o.disabledForEditor && o.disabledForEditor(reader)));
719
+ }
720
+ function sameOverrides(a, b) {
721
+ if (a.length !== b.length) {
722
+ return false;
723
+ }
724
+ for (let i = 0; i < a.length; i++) {
725
+ if (a[i] !== b[i]) {
726
+ return false;
727
+ }
728
+ }
729
+ return true;
730
+ }
731
+ function symmetricDiff(prev, next) {
732
+ const prevSet = new Set(prev);
733
+ const nextSet = new Set(next);
734
+ const changed = [];
735
+ for (const o of prev) {
736
+ if (!nextSet.has(o)) {
737
+ changed.push(o);
738
+ }
739
+ }
740
+ for (const o of next) {
741
+ if (!prevSet.has(o)) {
742
+ changed.push(o);
743
+ }
744
+ }
745
+ return changed;
746
+ }
747
+
748
+ /**
749
+ * Build a predicate matching the nodes an override targets — `'*'` matches
750
+ * everything, a node class matches by `instanceof`, and a guard is used as-is.
751
+ */
752
+ function nodeMatcher(o) {
753
+ if (o.nodes === '*') {
754
+ return () => true;
755
+ }
756
+ const matchers = o.nodes.map(match => {
757
+ const klass = match;
758
+ return lexical.$isLexicalNode(klass.prototype) ? node => node instanceof klass : match;
759
+ });
760
+ return node => matchers.some(f => f(node));
761
+ }
762
+
763
+ /**
764
+ * Build a predicate matching the nodes whose DOM must be recreated for the
765
+ * given override change, or `null` when no live re-render is needed.
766
+ *
767
+ * `$createDOM`/`$getDOMSlot` produce the element and slot, and `$decorateDOM`
768
+ * may add DOM that only a fresh `$createDOM` can revert — so toggling any of
769
+ * them recreates the affected nodes. `$updateDOM` is diff-driven and applies on
770
+ * the next node update, and export-only hooks ($exportDOM/$shouldInclude/…)
771
+ * don't touch the live DOM, so neither needs a re-render. Recreating every
772
+ * affected node is the simple, always-correct choice; toggles are rare, so the
773
+ * cost is acceptable and can be optimized later if needed.
774
+ */
775
+ function recreatePredicate(changed) {
776
+ const matchers = [];
777
+ for (const o of changed) {
778
+ if (o.$createDOM || o.$getDOMSlot || o.$decorateDOM) {
779
+ matchers.push(nodeMatcher(o));
780
+ }
781
+ }
782
+ return matchers.length === 0 ? null : node => matchers.some(f => f(node));
783
+ }
784
+
785
+ /**
786
+ * Per-editor runtime backing {@link DOMRenderExtension}'s conditional
787
+ * overrides and imperative editor context. See {@link DOMRenderRuntime}.
788
+ *
789
+ * @internal
790
+ */
791
+ class DOMRenderRuntimeImpl {
792
+ editor;
793
+ /**
794
+ * The `nodes` and base `dom` captured at `init` (before `dom` was
795
+ * overwritten with the compiled config) — the clean base for every recompile.
796
+ */
797
+ initialEditorConfig;
798
+ overrides;
799
+ editorContext;
800
+ hasSessionGates;
801
+ installed;
802
+
803
+ /** Memoized session configs keyed by the set of session-disabled overrides. */
804
+ sessionCache = new Map();
805
+ constructor(editor, initialEditorConfig, overrides, editorContext) {
806
+ this.editor = editor;
807
+ this.initialEditorConfig = initialEditorConfig;
808
+ this.overrides = overrides;
809
+ this.editorContext = editorContext;
810
+ this.installed = filterEditorInstalled(overrides, editorContext);
811
+ this.hasSessionGates = overrides.some(o => o.disabledForSession);
812
+ }
813
+ setContextValue(cfg, value) {
814
+ const prev = this.installed;
815
+ this.editorContext[cfg.key] = value;
816
+ const next = filterEditorInstalled(this.overrides, this.editorContext);
817
+ if (sameOverrides(prev, next)) {
818
+ return;
819
+ }
820
+ const changed = symmetricDiff(prev, next);
821
+ this.installed = next;
822
+ this.sessionCache.clear();
823
+ const dom = compileDOMRenderConfigOverrides(this.initialEditorConfig, {
824
+ overrides: next
825
+ });
826
+ this.editor._config.dom = dom;
827
+ const recreate = recreatePredicate(changed);
828
+ if (!recreate) {
829
+ // $updateDOM-only or export-only change: the recompiled config is enough.
830
+ return;
831
+ }
832
+
833
+ // Re-render through a full reconcile, which reuses the existing node
834
+ // instances (no node-map mutation, so no spurious mutation/collaboration
835
+ // changes). The affected nodes must be unmounted and recreated — the removed
836
+ // override may have produced or decorated DOM that only a fresh $createDOM
837
+ // reverts — so install a transient $updateDOM that reports a recreate for
838
+ // matching nodes.
839
+ //
840
+ // This mutates the (shared) active config, so the reconcile MUST run and
841
+ // finish synchronously before the original is restored on the next line —
842
+ // hence `discrete`, and hence this must not be called from within an
843
+ // editor.update (where the commit would defer). A deferred update would
844
+ // either restore the wrapper before the reconcile reads it (no recreate) or
845
+ // leave it armed across a window where an unrelated reconcile would
846
+ // spuriously recreate matching nodes. No history tag is needed: a full
847
+ // reconcile marks no nodes dirty, which history merges/discards without
848
+ // pushing.
849
+ const base = dom.$updateDOM;
850
+ dom.$updateDOM = (nextNode, prevNode, el, editor) => recreate(nextNode) ? true : base(nextNode, prevNode, el, editor);
851
+ this.editor.update(lexical.$fullReconcile, {
852
+ discrete: true
853
+ });
854
+ dom.$updateDOM = base;
855
+ }
856
+ getSessionConfig() {
857
+ const resident = this.editor._config.dom || lexical.DEFAULT_EDITOR_DOM_CONFIG;
858
+ if (!this.hasSessionGates) {
859
+ return resident;
860
+ }
861
+ const reader = makeReader(getContextRecord(DOMRenderContextSymbol, this.editor) || this.editorContext);
862
+ const disabledKeys = [];
863
+ const sessionSet = [];
864
+ this.installed.forEach((o, i) => {
865
+ if (o.disabledForSession && o.disabledForSession(reader)) {
866
+ disabledKeys.push(String(i));
867
+ } else {
868
+ sessionSet.push(o);
869
+ }
870
+ });
871
+ if (disabledKeys.length === 0) {
872
+ return resident;
873
+ }
874
+ const key = disabledKeys.join(',');
875
+ let cfg = this.sessionCache.get(key);
876
+ if (!cfg) {
877
+ cfg = compileDOMRenderConfigOverrides(this.initialEditorConfig, {
878
+ overrides: sessionSet
879
+ });
880
+ this.sessionCache.set(key, cfg);
881
+ }
882
+ return cfg;
883
+ }
884
+ }
885
+
886
+ /**
887
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
888
+ *
889
+ * This source code is licensed under the MIT license found in the
890
+ * LICENSE file in the root directory of this source tree.
891
+ *
892
+ */
893
+
894
+
895
+ /** @internal The result returned from {@link DOMRenderExtension}'s `init`. */
896
+
897
+ /**
898
+ * @experimental
899
+ *
900
+ * An extension that allows overriding the render and export behavior for an
901
+ * editor. This is highly experimental and subject to change from one version
902
+ * to the next.
903
+ **/
904
+ const DOMRenderExtension = lexical.defineExtension({
905
+ build(editor, config, state) {
906
+ const {
907
+ initialEditorConfig
908
+ } = state.getInitResult();
909
+ const editorContext = createEditorContextRecord(config.contextDefaults);
910
+ const runtime = new DOMRenderRuntimeImpl(editor, initialEditorConfig, config.overrides, editorContext);
911
+ return {
912
+ defaults: editorContext,
913
+ runtime
914
+ };
915
+ },
916
+ config: {
917
+ contextDefaults: [],
918
+ overrides: []
919
+ },
920
+ html: {
921
+ // Define a RootNode export for $generateDOMFromRoot
922
+ export: new Map([[lexical.RootNode, () => {
923
+ const element = document.createElement('div');
924
+ element.role = 'textbox';
925
+ return {
926
+ element
927
+ };
928
+ }]])
929
+ },
930
+ init(editorConfig, config) {
931
+ // Capture the user's base `dom` (before we overwrite it) and `nodes` so the
932
+ // runtime can recompile from scratch when overrides toggle.
933
+ const initialEditorConfig = {
934
+ dom: editorConfig.dom,
935
+ nodes: editorConfig.nodes
936
+ };
937
+ const editorContext = createEditorContextRecord(config.contextDefaults);
938
+ const installed = filterEditorInstalled(config.overrides, editorContext);
939
+ editorConfig.dom = compileDOMRenderConfigOverrides(editorConfig, {
940
+ overrides: installed
941
+ });
942
+ return {
943
+ initialEditorConfig
944
+ };
945
+ },
946
+ mergeConfig(config, partial) {
947
+ const merged = lexical.shallowMergeConfig(config, partial);
948
+ for (const k of ['overrides', 'contextDefaults']) {
949
+ if (partial[k]) {
950
+ merged[k] = [...config[k], ...partial[k]];
951
+ }
952
+ }
953
+ return merged;
954
+ },
955
+ name: DOMRenderExtensionName
956
+ });
957
+
958
+ /**
959
+ * @internal
960
+ *
961
+ * A predicate that may write into the per-invocation `captures` map. Returns
962
+ * `true` if the rule matches; `false` otherwise.
963
+ */
964
+
965
+ /** @internal */
966
+
967
+ /** @internal The runtime shape of a {@link CompiledSelector}. */
968
+
969
+ const IMPL = Symbol.for('@lexical/html/SelectorImpl');
970
+
971
+ /** @internal */
972
+ function getSelectorImpl(sel) {
973
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
974
+ const impl = sel[IMPL];
975
+ if (!(impl !== undefined)) {
976
+ formatDevErrorMessage(`match must be a CompiledSelector produced by sel.* or sel.css(); received a raw object.`);
977
+ }
978
+ return impl;
979
+ }
980
+ function combinePredicates(preds) {
981
+ if (preds.length === 0) {
982
+ return lexical.isHTMLElement;
983
+ }
984
+ if (preds.length === 1) {
985
+ return preds[0];
986
+ }
987
+ return (node, captures) => {
988
+ for (const p of preds) {
989
+ if (!p(node, captures)) {
990
+ return false;
991
+ }
992
+ }
993
+ return true;
994
+ };
995
+ }
996
+
997
+ /**
998
+ * @internal
999
+ *
1000
+ * Build a selector value from a tag set and a predicate list. Used by the
1001
+ * combinator API and the CSS parser.
1002
+ */
1003
+ function buildSelector(tags, predicates) {
1004
+ const impl = {
1005
+ kind: 'element',
1006
+ predicate: combinePredicates(predicates),
1007
+ tags
1008
+ };
1009
+ const refine = additional => buildSelector(tags, [...predicates, additional]);
1010
+ const builder = {
1011
+ [IMPL]: impl,
1012
+ attr: (name, value, options) => refine(buildAttrPredicate(name, value, options)),
1013
+ classAll: (...classes) => refine(buildClassAllPredicate(classes)),
1014
+ classAny: (...classes) => refine(buildClassAnyPredicate(classes)),
1015
+ styleAny: (prop, value, options) => refine(buildStylePredicate(prop, value, options))
1016
+ };
1017
+ // The runtime is fully type-erased; cast to satisfy the surface.
1018
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1019
+ return builder;
1020
+ }
1021
+ function normalizeClassList(classes) {
1022
+ const out = [];
1023
+ for (const c of classes) {
1024
+ if (c) {
1025
+ out.push(c);
1026
+ }
1027
+ }
1028
+ return out;
1029
+ }
1030
+
1031
+ /** @internal */
1032
+ function buildClassAllPredicate(classes) {
1033
+ const ns = normalizeClassList(classes);
1034
+ if (ns.length === 0) {
1035
+ return () => true;
1036
+ }
1037
+ return node => {
1038
+ if (!lexical.isHTMLElement(node)) {
1039
+ return false;
1040
+ }
1041
+ const cl = node.classList;
1042
+ for (const c of ns) {
1043
+ if (!cl.contains(c)) {
1044
+ return false;
1045
+ }
1046
+ }
1047
+ return true;
1048
+ };
1049
+ }
1050
+
1051
+ /** @internal */
1052
+ function buildClassAnyPredicate(classes) {
1053
+ const ns = normalizeClassList(classes);
1054
+ if (ns.length === 0) {
1055
+ return () => false;
1056
+ }
1057
+ return node => {
1058
+ if (!lexical.isHTMLElement(node)) {
1059
+ return false;
1060
+ }
1061
+ const cl = node.classList;
1062
+ for (const c of ns) {
1063
+ if (cl.contains(c)) {
1064
+ return true;
1065
+ }
1066
+ }
1067
+ return false;
1068
+ };
1069
+ }
1070
+
1071
+ /** @internal */
1072
+ function buildAttrPredicate(name, value, options) {
1073
+ if (value === true) {
1074
+ return node => lexical.isHTMLElement(node) && node.hasAttribute(name);
1075
+ }
1076
+ if (typeof value === 'string') {
1077
+ return node => lexical.isHTMLElement(node) && node.getAttribute(name) === value;
1078
+ }
1079
+ if (value instanceof RegExp) {
1080
+ const capture = options && options.capture;
1081
+ const re = value;
1082
+ return (node, captures) => {
1083
+ if (!lexical.isHTMLElement(node)) {
1084
+ return false;
1085
+ }
1086
+ const v = node.getAttribute(name);
1087
+ if (v == null) {
1088
+ return false;
1089
+ }
1090
+ const m = v.match(re);
1091
+ if (m === null) {
1092
+ return false;
1093
+ }
1094
+ if (capture !== undefined) {
1095
+ captures[capture] = m;
1096
+ }
1097
+ return true;
1098
+ };
1099
+ }
1100
+ {
1101
+ formatDevErrorMessage(`sel.attr(${JSON.stringify(name)}, ...) requires true, a string, or a RegExp`);
1102
+ }
1103
+ }
1104
+ function buildStylePredicate(prop, value, options) {
1105
+ if (typeof value === 'string') {
1106
+ return node => lexical.isHTMLElement(node) && node.style.getPropertyValue(prop) === value;
1107
+ }
1108
+ if (value instanceof RegExp) {
1109
+ const capture = options && options.capture;
1110
+ const re = value;
1111
+ return (node, captures) => {
1112
+ if (!lexical.isHTMLElement(node)) {
1113
+ return false;
1114
+ }
1115
+ const v = node.style.getPropertyValue(prop);
1116
+ if (!v) {
1117
+ return false;
1118
+ }
1119
+ const m = v.match(re);
1120
+ if (m === null) {
1121
+ return false;
1122
+ }
1123
+ if (capture !== undefined) {
1124
+ captures[capture] = m;
1125
+ }
1126
+ return true;
1127
+ };
1128
+ }
1129
+ {
1130
+ formatDevErrorMessage(`sel.styleAny(${JSON.stringify(prop)}, ...) requires a string or a RegExp`);
1131
+ }
1132
+ }
1133
+ const TEXT_SELECTOR_IMPL = {
1134
+ kind: 'text',
1135
+ predicate: lexical.isDOMTextNode,
1136
+ tags: new Set()
1137
+ };
1138
+
1139
+ // The `as` cast is needed because `CompiledSelector` is an opaque
1140
+ // branded interface — neither the object literal nor a typed const can
1141
+ // declare the internal `IMPL` symbol without exposing it.
1142
+ const TEXT_SELECTOR = {
1143
+ [IMPL]: TEXT_SELECTOR_IMPL
1144
+ };
1145
+ const COMMENT_SELECTOR_IMPL = {
1146
+ kind: 'comment',
1147
+ predicate: node => node.nodeType === 8 /* COMMENT_NODE */,
1148
+ tags: new Set()
1149
+ };
1150
+ const COMMENT_SELECTOR = {
1151
+ [IMPL]: COMMENT_SELECTOR_IMPL
1152
+ };
1153
+
1154
+ /**
1155
+ * Combinator API for building {@link CompiledSelector}s. The public
1156
+ * `sel` is augmented from this in `./index.ts` (where the CSS parser is
1157
+ * available without a circular import); consumers outside `@lexical/html`
1158
+ * should always import the public `sel` from the package root.
1159
+ *
1160
+ * @internal
1161
+ */
1162
+ const selBase = {
1163
+ /** Match any {@link HTMLElement}. */
1164
+ any() {
1165
+ return buildSelector(new Set(), []);
1166
+ },
1167
+ /** Match DOM {@link Comment} nodes. */
1168
+ comment() {
1169
+ return COMMENT_SELECTOR;
1170
+ },
1171
+ /**
1172
+ * Match by tag name(s). With one literal tag the element type is narrowed
1173
+ * (e.g. `'a' → HTMLAnchorElement`); with multiple, it is the union of
1174
+ * their `HTMLElementTagNameMap` entries.
1175
+ */
1176
+ tag(...tags) {
1177
+ if (!(tags.length > 0)) {
1178
+ formatDevErrorMessage(`sel.tag() requires at least one tag name`);
1179
+ }
1180
+ const upper = new Set();
1181
+ for (const t of tags) {
1182
+ upper.add(t.toUpperCase());
1183
+ }
1184
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1185
+ return buildSelector(upper, []);
1186
+ },
1187
+ /** Match DOM {@link Text} nodes. */
1188
+ text() {
1189
+ return TEXT_SELECTOR;
1190
+ }
1191
+ };
1192
+
1193
+ /**
1194
+ * Cross-frame-safe replacement for `node instanceof HTMLXxxElement`. Returns
1195
+ * true when `node` is an HTMLElement whose `nodeName` equals `tag` (compared
1196
+ * case-insensitively).
1197
+ *
1198
+ * @experimental
1199
+ */
1200
+ function isElementOfTag(node, tag) {
1201
+ return lexical.isHTMLElement(node) && node.nodeName === tag.toUpperCase();
1202
+ }
1203
+
1204
+ const IDENT_CHAR = /[A-Za-z0-9_-]/;
1205
+ class Cursor {
1206
+ constructor(source, pos) {
1207
+ this.source = source;
1208
+ this.pos = pos;
1209
+ }
1210
+ peek(offset = 0) {
1211
+ return this.source[this.pos + offset] || '';
1212
+ }
1213
+ consume() {
1214
+ return this.source[this.pos++] || '';
1215
+ }
1216
+ eof() {
1217
+ return this.pos >= this.source.length;
1218
+ }
1219
+ skipWhitespace() {
1220
+ while (!this.eof() && /\s/.test(this.peek())) {
1221
+ this.pos++;
1222
+ }
1223
+ }
1224
+ readIdent() {
1225
+ const start = this.pos;
1226
+ while (!this.eof() && IDENT_CHAR.test(this.peek())) {
1227
+ this.pos++;
1228
+ }
1229
+ return this.source.slice(start, this.pos);
1230
+ }
1231
+ readQuoted() {
1232
+ const quote = this.consume();
1233
+ this.assert(quote === '"' || quote === "'", 'expected quote');
1234
+ const start = this.pos;
1235
+ while (!this.eof() && this.peek() !== quote) {
1236
+ if (this.peek() === '\\') {
1237
+ this.pos += 2;
1238
+ } else {
1239
+ this.pos++;
1240
+ }
1241
+ }
1242
+ this.assert(!this.eof(), 'unterminated string');
1243
+ const value = this.source.slice(start, this.pos);
1244
+ this.pos++; // consume closing quote
1245
+ return value.replace(/\\(.)/g, '$1');
1246
+ }
1247
+ /**
1248
+ * `invariant(cond, fmt, …)`-flavored assertion that also surfaces the
1249
+ * cursor's position context. Use for parse-time errors so a malformed
1250
+ * CSS selector gets a useful, position-annotated message.
1251
+ */
1252
+ assert(cond, msg) {
1253
+ if (!cond) {
1254
+ formatDevErrorMessage(`invalid CSS selector at col ${String(this.pos + 1)}: ${msg} in ${this.source}`);
1255
+ }
1256
+ }
1257
+ }
1258
+ function parseSimpleSelector(c) {
1259
+ const tags = new Set();
1260
+ const predicates = [];
1261
+ const classes = [];
1262
+ c.skipWhitespace();
1263
+
1264
+ // Optional tag or '*'
1265
+ if (c.peek() === '*') {
1266
+ c.consume();
1267
+ } else if (IDENT_CHAR.test(c.peek())) {
1268
+ const tag = c.readIdent();
1269
+ if (tag) {
1270
+ tags.add(tag.toUpperCase());
1271
+ }
1272
+ }
1273
+
1274
+ // Zero or more refinements: .class, #id, [attr]
1275
+ while (!c.eof()) {
1276
+ const ch = c.peek();
1277
+ if (ch === '.') {
1278
+ c.consume();
1279
+ const cls = c.readIdent();
1280
+ c.assert(cls !== '', 'expected class name after "."');
1281
+ classes.push(cls);
1282
+ } else if (ch === '#') {
1283
+ c.consume();
1284
+ const id = c.readIdent();
1285
+ c.assert(id !== '', 'expected id after "#"');
1286
+ predicates.push(buildAttrPredicate('id', id));
1287
+ } else if (ch === '[') {
1288
+ c.consume();
1289
+ c.skipWhitespace();
1290
+ const name = c.readIdent();
1291
+ c.assert(name !== '', 'expected attribute name after "["');
1292
+ c.skipWhitespace();
1293
+ let value = true;
1294
+ if (c.peek() === '=') {
1295
+ c.consume();
1296
+ c.skipWhitespace();
1297
+ const next = c.peek();
1298
+ if (next === '"' || next === "'") {
1299
+ value = c.readQuoted();
1300
+ } else {
1301
+ value = c.readIdent();
1302
+ c.assert(value !== '', 'expected attribute value');
1303
+ }
1304
+ c.skipWhitespace();
1305
+ }
1306
+ c.assert(c.peek() === ']', 'expected "]"');
1307
+ c.consume();
1308
+ predicates.push(buildAttrPredicate(name, value));
1309
+ } else {
1310
+ break;
1311
+ }
1312
+ }
1313
+ if (classes.length > 0) {
1314
+ predicates.push(buildClassAllPredicate(classes));
1315
+ }
1316
+ return {
1317
+ predicates,
1318
+ tags
1319
+ };
1320
+ }
1321
+
1322
+ /**
1323
+ * Parse a reduced CSS-selector subset and return a {@link CompiledSelector}.
1324
+ * Supported:
1325
+ * - Tag (`p`), wildcard (`*`).
1326
+ * - Tag list (`h1, h2, h3`).
1327
+ * - Class (`.foo`, `.foo.bar`).
1328
+ * - ID (`#foo`).
1329
+ * - Attribute presence (`[name]`).
1330
+ * - Attribute equality (`[name="value"]`, `[name=value]`).
1331
+ *
1332
+ * Anything outside the subset (regex attribute, inline-style match,
1333
+ * combinators, pseudo-classes) is intentionally rejected — chain combinator
1334
+ * methods off the returned builder instead.
1335
+ *
1336
+ * @experimental
1337
+ */
1338
+ function parseSelector(source) {
1339
+ const c = new Cursor(source, 0);
1340
+ const groups = [];
1341
+ while (true) {
1342
+ const group = parseSimpleSelector(c);
1343
+ groups.push(group);
1344
+ c.skipWhitespace();
1345
+ if (c.eof()) {
1346
+ break;
1347
+ }
1348
+ c.assert(c.peek() === ',', 'expected "," (selector lists are the only supported combinator)');
1349
+ c.consume();
1350
+ c.skipWhitespace();
1351
+ }
1352
+ if (groups.length === 1) {
1353
+ return buildSelector(groups[0].tags, groups[0].predicates);
1354
+ }
1355
+
1356
+ // Comma-separated list. Merge tag sets and OR-combine the per-group
1357
+ // refinement predicates so that each candidate node satisfies *some*
1358
+ // group entirely.
1359
+ const tags = new Set();
1360
+ for (const g of groups) {
1361
+ for (const t of g.tags) {
1362
+ tags.add(t);
1363
+ }
1364
+ }
1365
+ const orPredicate = (node, captures) => {
1366
+ for (const g of groups) {
1367
+ const upper = node.nodeName;
1368
+ if (g.tags.size > 0 && !g.tags.has(upper)) {
1369
+ continue;
1370
+ }
1371
+ let ok = true;
1372
+ for (const p of g.predicates) {
1373
+ if (!p(node, captures)) {
1374
+ ok = false;
1375
+ break;
1376
+ }
1377
+ }
1378
+ if (ok) {
1379
+ return true;
1380
+ }
1381
+ }
1382
+ return false;
1383
+ };
1384
+ return buildSelector(tags, [orPredicate]);
1385
+ }
1386
+
1387
+ /**
1388
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1389
+ *
1390
+ * This source code is licensed under the MIT license found in the
1391
+ * LICENSE file in the root directory of this source tree.
1392
+ *
1393
+ */
1394
+
1395
+ /**
1396
+ * Identity helper that infers a rule's matched node type and capture map
1397
+ * from its `match` selector and threads them into the `$import` signature.
1398
+ * Usage:
1399
+ *
1400
+ * ```ts
1401
+ * defineImportRule({
1402
+ * name: '@lexical/list/li',
1403
+ * match: sel.tag('li'),
1404
+ * $import: (ctx, el, $next) => {
1405
+ * // el: HTMLLIElement
1406
+ * return [$createListItemNode()];
1407
+ * },
1408
+ * });
1409
+ * ```
1410
+ *
1411
+ * @experimental
1412
+ * @__NO_SIDE_EFFECTS__
1413
+ */
1414
+ function defineImportRule(rule) {
1415
+ return rule;
1416
+ }
1417
+
1418
+ /**
1419
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1420
+ *
1421
+ * This source code is licensed under the MIT license found in the
1422
+ * LICENSE file in the root directory of this source tree.
1423
+ *
1424
+ */
1425
+
1426
+ /**
1427
+ * Create an import context state. The phantom symbol prevents accidental
1428
+ * use of a render-context state in an import context (and vice versa).
1429
+ *
1430
+ * Note: to support the value-or-updater pattern, `V` cannot be a function
1431
+ * type; wrap it in an array or object if needed.
1432
+ *
1433
+ * `getDefaultValue` is called **once at state creation** and the result is
1434
+ * shared between every session that reads the state without first writing
1435
+ * a value. Defaults must therefore be immutable (primitives, frozen
1436
+ * objects, or read-only arrays / records). If your state needs mutable
1437
+ * per-session storage, lazily initialize it inside your rule (e.g.
1438
+ * `if (!ctx.session.has(cfg)) ctx.session.set(cfg, new …())`).
1439
+ *
1440
+ * @experimental
1441
+ * @__NO_SIDE_EFFECTS__
1442
+ */
1443
+ function createImportState(name, getDefaultValue, isEqual) {
1444
+ return createContextState(DOMImportContextSymbol, name, getDefaultValue, isEqual);
1445
+ }
1446
+
1447
+ /**
1448
+ * The kind of operation that produced this import. Lets rules adapt
1449
+ * their behavior (e.g. preserve more whitespace on `'paste'`).
1450
+ * Defaults to `'unknown'`. Apps that need a different vocabulary can
1451
+ * define their own {@link ImportStateConfig} with whatever value type
1452
+ * they want.
1453
+ *
1454
+ * @experimental
1455
+ */
1456
+
1457
+ /**
1458
+ * Built-in import-context state identifying how this import was initiated.
1459
+ * Callers of `$generateNodesFromDOM` should set it via the `context` option.
1460
+ *
1461
+ * @experimental
1462
+ */
1463
+ const ImportSource = createImportState('importSource', () => 'unknown');
1464
+
1465
+ /**
1466
+ * Built-in import-context state holding the {@link DataTransfer} the
1467
+ * import was sourced from, if any. `null` outside paste/drop flows.
1468
+ *
1469
+ * The clipboard import pipeline passes the original `DataTransfer`
1470
+ * through to its per-MIME-type handler stack (see
1471
+ * {@link ImportMimeTypeFunction}); handlers that route HTML through
1472
+ * the {@link DOMImportExtension} pipeline should forward it into the
1473
+ * walk via `context: [contextValue(ImportSourceDataTransfer,
1474
+ * dataTransfer)]` so rules and preprocessors can call
1475
+ * `ctx.get(ImportSourceDataTransfer)` to inspect companion MIME types
1476
+ * (e.g. an `'application/rtf'` alternative or an attached
1477
+ * `'application/x-officedrawing'` payload), the file list, or any
1478
+ * custom drag-and-drop slot.
1479
+ *
1480
+ * Use sparingly: the safer pattern is to decide *which* MIME-type
1481
+ * payload to walk in the clipboard handler stack and hand a finalized
1482
+ * DOM to the rules; only fall back to peeking at `ImportSourceDataTransfer`
1483
+ * when the source-detection signal genuinely lives in a companion
1484
+ * slot.
1485
+ *
1486
+ * @experimental
1487
+ */
1488
+ const ImportSourceDataTransfer = createImportState('importSourceDataTransfer', () => null);
1489
+
1490
+ /**
1491
+ * Built-in import-context state holding the bit-packed
1492
+ * {@link TextFormatType} formats that should apply to {@link TextNode}s
1493
+ * produced during the current subtree. Used by inline-format wrappers
1494
+ * (`<b>`, `<i>`, `<u>`, …) to propagate formatting through the context
1495
+ * record instead of via the legacy `forChild` chain.
1496
+ *
1497
+ * @experimental
1498
+ */
1499
+ const ImportTextFormat = createImportState('textFormat', () => 0);
1500
+
1501
+ /**
1502
+ * Built-in import-context state holding a parsed CSS-style record
1503
+ * (the {@link getStyleObjectFromCSS} shape) that should apply to
1504
+ * {@link TextNode}s produced during the current subtree. Mirrors the
1505
+ * format-bit propagation in {@link ImportTextFormat} for properties
1506
+ * that don't fit into the format bit mask — `color`, `font-family`,
1507
+ * `font-size`, etc.
1508
+ *
1509
+ * Ancestor rules that contribute a style branch the context with a
1510
+ * merged record; the core `#text` rule materializes the non-empty
1511
+ * record to a CSS string and calls `setStyle` on the new TextNode.
1512
+ * Once TextNode adopts a parsed style record, the materialization
1513
+ * step will go away.
1514
+ *
1515
+ * @experimental
1516
+ */
1517
+ const ImportTextStyle = createImportState('textStyle', () => ({}));
1518
+
1519
+ /**
1520
+ * Determines whether a given DOM element should be treated as preserving
1521
+ * whitespace (i.e. text content under it is not collapsed and is split on
1522
+ * `\n` / `\t` into `LineBreakNode` / `TabNode`). The default matches the
1523
+ * legacy behavior: the element itself is `<pre>` or its inline
1524
+ * `white-space` style begins with `'pre'`.
1525
+ *
1526
+ * @experimental
1527
+ */
1528
+
1529
+ /**
1530
+ * Determines whether a given DOM node sits on the same visual line as its
1531
+ * adjacent text siblings, governing whether leading/trailing whitespace in
1532
+ * a `#text` is collapsed against neighbors. The default consults
1533
+ * {@link isInlineDomNode} from `lexical` (style.display or a fixed inline
1534
+ * tag-name set) and additionally treats elements with an explicit
1535
+ * non-inline `display` style as block.
1536
+ *
1537
+ * @experimental
1538
+ */
1539
+
1540
+ /**
1541
+ * Configuration for the core text whitespace-collapse logic. Override via
1542
+ * {@link ImportWhitespaceConfig} either as a `contextDefaults` entry on
1543
+ * the {@link DOMImportExtension} or per-call on `$generateNodesFromDOM`'s
1544
+ * `context` option.
1545
+ *
1546
+ * @experimental
1547
+ */
1548
+
1549
+ /**
1550
+ * Default {@link WhitespaceImportConfig.preservesWhitespace}: matches
1551
+ * `<pre>` and any element with `white-space: pre*`.
1552
+ *
1553
+ * @experimental
1554
+ */
1555
+ function defaultPreservesWhitespace(node) {
1556
+ if (!lexical.isHTMLElement(node)) {
1557
+ return false;
1558
+ }
1559
+ if (node.nodeName === 'PRE') {
1560
+ return true;
1561
+ }
1562
+ const ws = node.style.whiteSpace;
1563
+ return typeof ws === 'string' && ws.startsWith('pre');
1564
+ }
1565
+
1566
+ /**
1567
+ * Default {@link WhitespaceImportConfig.isInline}: treats an element as
1568
+ * inline iff its inline `display` style is `inline*` OR (no explicit
1569
+ * non-inline display) its nodeName is a known inline tag (`isInlineDomNode`).
1570
+ * Text nodes are always inline; comments and other non-elements are not.
1571
+ *
1572
+ * @experimental
1573
+ */
1574
+ function defaultIsInline(node) {
1575
+ if (lexical.isDOMTextNode(node)) {
1576
+ return true;
1577
+ }
1578
+ if (!lexical.isHTMLElement(node)) {
1579
+ return false;
1580
+ }
1581
+ const display = node.style.display;
1582
+ if (display) {
1583
+ return display.startsWith('inline');
1584
+ }
1585
+ if (lexical.isBlockDomNode(node)) {
1586
+ return false;
1587
+ }
1588
+ return lexical.isInlineDomNode(node);
1589
+ }
1590
+
1591
+ /**
1592
+ * Built-in import-context state controlling text-node whitespace handling
1593
+ * (collapse vs. preserve, what counts as an inline sibling). Override per
1594
+ * editor via {@link DOMImportConfig.contextDefaults} or per call via
1595
+ * {@link GenerateNodesFromDOMOptions.context}.
1596
+ *
1597
+ * @experimental
1598
+ */
1599
+ const ImportWhitespaceConfig = createImportState('whitespaceConfig', () => ({
1600
+ isInline: defaultIsInline,
1601
+ preservesWhitespace: defaultPreservesWhitespace
1602
+ }));
1603
+
1604
+ /**
1605
+ * Built-in session slot for runtime overlay rules that should be in
1606
+ * effect for the entire walk. A preprocessor writes here when it wants
1607
+ * to conditionally install handling for a particular paste source
1608
+ * (e.g. "if the Microsoft Word generator meta tag is present, push the
1609
+ * Word-paste overlay"). Each entry contributes an overlay dispatcher
1610
+ * to the runtime's overlay stack; later array entries are higher
1611
+ * priority. Use `ctx.session.update(ImportOverlays, prev => […])` to
1612
+ * append.
1613
+ *
1614
+ * This is the walk-wide counterpart to
1615
+ * `$importChildren({rules: …})` (which scopes an overlay to one
1616
+ * subtree): write to {@link ImportOverlays} when the overlay should
1617
+ * apply for the whole document; use `$importChildren`'s `rules` when
1618
+ * the overlay should only apply for a deeper region.
1619
+ *
1620
+ * @experimental
1621
+ */
1622
+ const ImportOverlays = createImportState('importOverlays', () => []);
1623
+
1624
+ /**
1625
+ * The session IS the root-layer {@link ContextRecord} of the walk. Reads
1626
+ * fall through the prototype chain to the editor's `contextDefaults`,
1627
+ * writes mutate the record's own properties, and any branch pushed by
1628
+ * `$importChildren({context})` sits above this layer and can shadow
1629
+ * (but does not overwrite) slots.
1630
+ *
1631
+ * @internal
1632
+ */
1633
+ class ImportSessionImpl {
1634
+ constructor(record) {
1635
+ this.record = record;
1636
+ }
1637
+ get(cfg) {
1638
+ return getContextValue(this.record, cfg);
1639
+ }
1640
+ set(cfg, value) {
1641
+ this.record[cfg.key] = value;
1642
+ }
1643
+ update(cfg, updater) {
1644
+ this.record[cfg.key] = updater(getContextValue(this.record, cfg));
1645
+ }
1646
+ has(cfg) {
1647
+ return Object.prototype.hasOwnProperty.call(this.record, cfg.key);
1648
+ }
1649
+ }
1650
+ function getDefaultImportContext(editor) {
1651
+ const dep = extension.getPeerDependencyFromEditor(editor, DOMImportExtensionName);
1652
+ return dep ? dep.output.defaults : undefined;
1653
+ }
1654
+ function getImportContext(editor) {
1655
+ return getContextRecord(DOMImportContextSymbol, editor) || getDefaultImportContext(editor);
1656
+ }
1657
+
1658
+ /**
1659
+ * Read an import context value during an import operation.
1660
+ * @experimental
1661
+ */
1662
+ function $getImportContextValue(cfg, editor = lexical.$getEditor()) {
1663
+ return getContextValue(getImportContext(editor), cfg);
1664
+ }
1665
+
1666
+ /**
1667
+ * Run `f` with the given context pairs applied on top of the editor's
1668
+ * current import context.
1669
+ *
1670
+ * @experimental
1671
+ */
1672
+ const $withImportContext = $withContext(DOMImportContextSymbol, getDefaultImportContext);
1673
+
1674
+ /**
1675
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
1676
+ *
1677
+ * This source code is licensed under the MIT license found in the
1678
+ * LICENSE file in the root directory of this source tree.
1679
+ *
1680
+ */
1681
+ const sel$1 = selBase;
1682
+ const ALIGNMENT_VALUES = new Set(['center', 'end', 'justify', 'left', 'right', 'start']);
1683
+
1684
+ /**
1685
+ * True if `value` is a non-empty {@link ElementFormatType} (matches one of
1686
+ * the supported `text-align` / legacy `align`-attribute values).
1687
+ *
1688
+ * @internal
1689
+ */
1690
+ function isAlignmentValue(value) {
1691
+ return ALIGNMENT_VALUES.has(value);
1692
+ }
1693
+
1694
+ /**
1695
+ * A pair of bitmasks describing which {@link TextFormatType} bits to set
1696
+ * and which to clear when descending into an element. The clear pass
1697
+ * matters for cases the legacy OR-merge mishandled, e.g. `<b
1698
+ * style="font-weight: normal">` clearing an inherited bold, or `<sub>` /
1699
+ * `<sup>` clearing each other.
1700
+ */
1701
+
1702
+ /**
1703
+ * The small subset of inline-style properties that affect text formatting
1704
+ * during import. Modeled as a plain object so tag-implicit defaults and
1705
+ * the element's own inline `style` can be merged with `{...defaults,
1706
+ * ...override-if-set}` semantics rather than relying on CSSStyleDeclaration.
1707
+ */
1708
+
1709
+ /**
1710
+ * Default style implied by each inline format tag. `<b>`/`<strong>` set
1711
+ * font-weight, `<sub>` sets vertical-align, etc. Any of these can be
1712
+ * overridden by the element's own inline `style` (so `<b
1713
+ * style="font-weight: normal">` ends up with `fontWeight: 'normal'` in
1714
+ * the effective style).
1715
+ */
1716
+ const TAG_DEFAULT_STYLE = {
1717
+ B: {
1718
+ fontWeight: 'bold'
1719
+ },
1720
+ EM: {
1721
+ fontStyle: 'italic'
1722
+ },
1723
+ I: {
1724
+ fontStyle: 'italic'
1725
+ },
1726
+ S: {
1727
+ textDecoration: 'line-through'
1728
+ },
1729
+ STRONG: {
1730
+ fontWeight: 'bold'
1731
+ },
1732
+ SUB: {
1733
+ verticalAlign: 'sub'
1734
+ },
1735
+ SUP: {
1736
+ verticalAlign: 'super'
1737
+ },
1738
+ U: {
1739
+ textDecoration: 'underline'
1740
+ }
1741
+ };
1742
+
1743
+ /**
1744
+ * Tags whose effect on TextFormat has no CSS analog (so the style-merge
1745
+ * path can't reach them). Applied as a pure "set" override.
1746
+ */
1747
+ const TAG_ONLY_SET = {
1748
+ CODE: lexical.IS_CODE,
1749
+ MARK: lexical.IS_HIGHLIGHT
1750
+ };
1751
+ function readElementFormatStyle(el) {
1752
+ return {
1753
+ fontStyle: el.style.fontStyle,
1754
+ fontWeight: el.style.fontWeight,
1755
+ textDecoration: el.style.textDecoration,
1756
+ verticalAlign: el.style.verticalAlign
1757
+ };
1758
+ }
1759
+ function mergeStyles(defaults, override) {
1760
+ return {
1761
+ fontStyle: override.fontStyle || defaults.fontStyle,
1762
+ fontWeight: override.fontWeight || defaults.fontWeight,
1763
+ textDecoration: override.textDecoration || defaults.textDecoration,
1764
+ verticalAlign: override.verticalAlign || defaults.verticalAlign
1765
+ };
1766
+ }
1767
+
1768
+ /**
1769
+ * The CSS property names {@link styleFormatOverride} reads — these are
1770
+ * "owned" by {@link ImportTextFormat} (the bit mask). When the
1771
+ * {@link ImportTextStyle} record is materialized onto a TextNode's
1772
+ * inline style by {@link styleObjectToCSS}, these are skipped so the
1773
+ * bit-mask side is the single source of truth and the same property
1774
+ * doesn't end up in both places (where the inline-style version would
1775
+ * shadow the format's themed CSS).
1776
+ */
1777
+ const FORMAT_BIT_STYLE_PROPS = new Set(['font-weight', 'font-style', 'text-decoration', 'vertical-align']);
1778
+
1779
+ /**
1780
+ * Translate a {@link FormatStyle} into a {@link FormatOverride}. Explicit
1781
+ * "non-decorating" values (`font-weight: normal`, `text-decoration: none`,
1782
+ * `vertical-align: baseline`) produce `clear` bits, so an inner element
1783
+ * can remove a format inherited from its ancestors.
1784
+ */
1785
+ function styleFormatOverride(style) {
1786
+ let set = 0;
1787
+ let clear = 0;
1788
+ const {
1789
+ fontWeight,
1790
+ fontStyle,
1791
+ textDecoration,
1792
+ verticalAlign
1793
+ } = style;
1794
+ if (fontWeight === '700' || fontWeight === 'bold') {
1795
+ set |= lexical.IS_BOLD;
1796
+ } else if (fontWeight === 'normal' || fontWeight === '400') {
1797
+ clear |= lexical.IS_BOLD;
1798
+ }
1799
+ if (fontStyle === 'italic') {
1800
+ set |= lexical.IS_ITALIC;
1801
+ } else if (fontStyle === 'normal') {
1802
+ clear |= lexical.IS_ITALIC;
1803
+ }
1804
+ if (textDecoration) {
1805
+ const parts = textDecoration.split(' ');
1806
+ if (parts.includes('underline')) {
1807
+ set |= lexical.IS_UNDERLINE;
1808
+ }
1809
+ if (parts.includes('line-through')) {
1810
+ set |= lexical.IS_STRIKETHROUGH;
1811
+ }
1812
+ if (parts.includes('none')) {
1813
+ clear |= lexical.IS_UNDERLINE | lexical.IS_STRIKETHROUGH;
1814
+ }
1815
+ }
1816
+ if (verticalAlign === 'sub') {
1817
+ set |= lexical.IS_SUBSCRIPT;
1818
+ clear |= lexical.IS_SUPERSCRIPT;
1819
+ } else if (verticalAlign === 'super') {
1820
+ set |= lexical.IS_SUPERSCRIPT;
1821
+ clear |= lexical.IS_SUBSCRIPT;
1822
+ } else if (verticalAlign === 'baseline') {
1823
+ clear |= lexical.IS_SUBSCRIPT | lexical.IS_SUPERSCRIPT;
1824
+ }
1825
+ return {
1826
+ clear,
1827
+ set
1828
+ };
1829
+ }
1830
+ function applyFormatOverride(format, ov) {
1831
+ return format & ~ov.clear | ov.set;
1832
+ }
1833
+
1834
+ /**
1835
+ * Unified rule for inline-format-bearing tags and `<span>`. The element's
1836
+ * effective style is its tag's {@link TAG_DEFAULT_STYLE} merged with its
1837
+ * inline `style` (element's own style wins for any property it sets), and
1838
+ * the resulting style is translated into a {@link FormatOverride}. Tags
1839
+ * with no CSS analog (`<code>`, `<mark>`) contribute their bit as a pure
1840
+ * `set` override.
1841
+ *
1842
+ * This shape lets:
1843
+ * - `<b style="font-weight: normal">` clear an inherited IS_BOLD.
1844
+ * - `<sub><sup>x</sup></sub>` resolve to IS_SUPERSCRIPT only (sub/sup
1845
+ * mutex via the vertical-align clear logic).
1846
+ * - `<span style="text-decoration: none">` strip inherited underline /
1847
+ * line-through.
1848
+ */
1849
+ const InlineFormatRule = defineImportRule({
1850
+ $import: (ctx, el) => {
1851
+ const inherited = ctx.get(ImportTextFormat);
1852
+ const tagDefault = TAG_DEFAULT_STYLE[el.nodeName];
1853
+ const elStyle = readElementFormatStyle(el);
1854
+ const effective = tagDefault ? mergeStyles(tagDefault, elStyle) : elStyle;
1855
+ let merged = applyFormatOverride(inherited, styleFormatOverride(effective));
1856
+ const tagOnly = TAG_ONLY_SET[el.nodeName];
1857
+ if (tagOnly) {
1858
+ merged |= tagOnly;
1859
+ }
1860
+ if (merged === inherited) {
1861
+ return ctx.$importChildren(el);
1862
+ }
1863
+ return ctx.$importChildren(el, {
1864
+ context: [contextValue(ImportTextFormat, merged)]
1865
+ });
1866
+ },
1867
+ match: sel$1.tag('b', 'strong', 'em', 'i', 'code', 'mark', 's', 'sub', 'sup', 'u', 'span'),
1868
+ name: '@lexical/html/inline-format'
1869
+ });
1870
+
1871
+ /**
1872
+ * Walk up the DOM ancestor chain to determine whether `node` is inside an
1873
+ * element whose whitespace should be preserved, per the supplied
1874
+ * {@link WhitespaceImportConfig.preservesWhitespace} predicate. Pure
1875
+ * ancestor walk, no caching.
1876
+ */
1877
+ function isInsidePreserveWhitespace(node, wsConfig) {
1878
+ let current = node.parentNode;
1879
+ while (current !== null) {
1880
+ if (wsConfig.preservesWhitespace(current)) {
1881
+ return true;
1882
+ }
1883
+ current = current.parentNode;
1884
+ }
1885
+ return false;
1886
+ }
1887
+ function findAdjacentTextOnLine(text, forward, wsConfig) {
1888
+ let node = text;
1889
+ while (true) {
1890
+ let sibling = null;
1891
+ while ((sibling = forward ? node.nextSibling : node.previousSibling) === null) {
1892
+ const parent = node.parentNode;
1893
+ if (parent === null) {
1894
+ return null;
1895
+ }
1896
+ node = parent;
1897
+ }
1898
+ node = sibling;
1899
+ if (!wsConfig.isInline(node)) {
1900
+ return null;
1901
+ }
1902
+ let descendant = node;
1903
+ while ((descendant = forward ? node.firstChild : node.lastChild) !== null) {
1904
+ node = descendant;
1905
+ }
1906
+ if (lexical.isDOMTextNode(node)) {
1907
+ return node;
1908
+ }
1909
+ if (node.nodeName === 'BR') {
1910
+ return null;
1911
+ }
1912
+ }
1913
+ }
1914
+ function collapseWhitespace(textNode, wsConfig) {
1915
+ let textContent = (textNode.textContent || '').replace(/\r/g, '').replace(/[ \t\n]+/g, ' ');
1916
+ if (textContent.length === 0) {
1917
+ return '';
1918
+ }
1919
+ if (textContent[0] === ' ') {
1920
+ let neighbor = textNode;
1921
+ let isStartOfLine = true;
1922
+ while (neighbor !== null && (neighbor = findAdjacentTextOnLine(neighbor, false, wsConfig)) !== null) {
1923
+ const neighborContent = neighbor.textContent || '';
1924
+ if (neighborContent.length > 0) {
1925
+ if (/[ \t\n]$/.test(neighborContent)) {
1926
+ textContent = textContent.slice(1);
1927
+ }
1928
+ isStartOfLine = false;
1929
+ break;
1930
+ }
1931
+ }
1932
+ if (isStartOfLine) {
1933
+ textContent = textContent.slice(1);
1934
+ }
1935
+ }
1936
+ if (textContent.length > 0 && textContent[textContent.length - 1] === ' ') {
1937
+ let neighbor = textNode;
1938
+ let isEndOfLine = true;
1939
+ while (neighbor !== null && (neighbor = findAdjacentTextOnLine(neighbor, true, wsConfig)) !== null) {
1940
+ const neighborContent = (neighbor.textContent || '').replace(/^( |\t|\r?\n)+/, '');
1941
+ if (neighborContent.length > 0) {
1942
+ isEndOfLine = false;
1943
+ break;
1944
+ }
1945
+ }
1946
+ if (isEndOfLine) {
1947
+ textContent = textContent.slice(0, -1);
1948
+ }
1949
+ }
1950
+ return textContent;
1951
+ }
1952
+ function $applyFormat(node, format) {
1953
+ return format !== 0 && lexical.$isTextNode(node) ? node.setFormat(format) : node;
1954
+ }
1955
+
1956
+ /**
1957
+ * Inverse of {@link getStyleObjectFromCSS}: serialize a parsed style
1958
+ * record back into a CSS declaration string suitable for
1959
+ * `TextNode.setStyle`. Returns the empty string for an empty record.
1960
+ */
1961
+ function styleObjectToCSS(style) {
1962
+ let css = '';
1963
+ for (const prop in style) {
1964
+ if (FORMAT_BIT_STYLE_PROPS.has(prop)) {
1965
+ // Owned by ImportTextFormat (bit mask) — skip so the format-bit
1966
+ // CSS is the single source of truth on the rendered TextNode.
1967
+ continue;
1968
+ }
1969
+ css += `${prop}: ${style[prop]}; `;
1970
+ }
1971
+ return css.trimEnd();
1972
+ }
1973
+ function $applyTextStyle(node, style) {
1974
+ if (lexical.$isTextNode(node)) {
1975
+ const css = styleObjectToCSS(style);
1976
+ if (css !== '') {
1977
+ node.setStyle(css);
1978
+ }
1979
+ }
1980
+ return node;
1981
+ }
1982
+
1983
+ /**
1984
+ * `#text` rule. Inside a `<pre>` ancestor, preserve whitespace and split
1985
+ * on `\n` and `\t` into `LineBreakNode`/`TabNode` siblings. Otherwise
1986
+ * collapse whitespace using the same neighbor-aware rules as the legacy
1987
+ * `$convertTextDOMNode`.
1988
+ */
1989
+ const TextRule = defineImportRule({
1990
+ $import: (ctx, el) => {
1991
+ const format = ctx.get(ImportTextFormat);
1992
+ const style = ctx.get(ImportTextStyle);
1993
+ const wsConfig = ctx.get(ImportWhitespaceConfig);
1994
+ if (isInsidePreserveWhitespace(el, wsConfig)) {
1995
+ const out = lexical.$generateNodesFromRawText(el.textContent || '');
1996
+ for (const node of out) {
1997
+ $applyFormat(node, format);
1998
+ $applyTextStyle(node, style);
1999
+ }
2000
+ return out;
2001
+ }
2002
+ const collapsed = collapseWhitespace(el, wsConfig);
2003
+ if (collapsed === '') {
2004
+ return [];
2005
+ }
2006
+ const text = lexical.$createTextNode(collapsed);
2007
+ $applyFormat(text, format);
2008
+ $applyTextStyle(text, style);
2009
+ return [text];
2010
+ },
2011
+ match: sel$1.text(),
2012
+ name: '@lexical/html/#text'
2013
+ });
2014
+
2015
+ /**
2016
+ * Drop `<style>` and `<script>` and skip descending into them — matches
2017
+ * the legacy `IGNORE_TAGS` set, but as a regular rule so apps can register
2018
+ * a higher-priority `<style>` rule to capture stylesheet text into the
2019
+ * import session for later use.
2020
+ */
2021
+ const IgnoreScriptStyleRule = defineImportRule({
2022
+ $import: () => [],
2023
+ match: sel$1.tag('script', 'style'),
2024
+ name: '@lexical/html/script-style-ignore'
2025
+ });
2026
+ const LineBreakRule = defineImportRule({
2027
+ $import: () => [lexical.$createLineBreakNode()],
2028
+ match: sel$1.tag('br'),
2029
+ name: '@lexical/html/br'
2030
+ });
2031
+
2032
+ /**
2033
+ * `<p>` rule. Re-applies format, indent, direction, and the legacy
2034
+ * `align` attribute fallback.
2035
+ */
2036
+ const ParagraphRule = defineImportRule({
2037
+ $import: (ctx, el) => {
2038
+ const p = lexical.$createParagraphNode();
2039
+ lexical.$setFormatFromDOM(p, el);
2040
+ lexical.setNodeIndentFromDOM(el, p);
2041
+ if (p.getFormatType() === '') {
2042
+ const align = el.getAttribute('align');
2043
+ if (align && isAlignmentValue(align)) {
2044
+ p.setFormat(align);
2045
+ }
2046
+ }
2047
+ lexical.$setDirectionFromDOM(p, el);
2048
+ // We deliberately pass no schema: paragraphs accept any inline run as-is.
2049
+ // The enclosing context (root / block) is responsible for ensuring the
2050
+ // paragraph itself is a valid block child.
2051
+ return [p.splice(0, 0, ctx.$importChildren(el))];
2052
+ },
2053
+ match: sel$1.tag('p'),
2054
+ name: '@lexical/html/p'
2055
+ });
2056
+
2057
+ /**
2058
+ * Rules covering the {@link ParagraphNode}, {@link TextNode},
2059
+ * {@link LineBreakNode}, and {@link TabNode} cases that the legacy
2060
+ * `importDOM` machinery in `@lexical/lexical` handled. Intended to be
2061
+ * registered as a dependency of every editor that uses
2062
+ * {@link DOMImportExtension}.
2063
+ *
2064
+ * @experimental
2065
+ */
2066
+ const CoreImportRules = [IgnoreScriptStyleRule, ParagraphRule, TextRule, LineBreakRule, InlineFormatRule];
2067
+
2068
+ /**
2069
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2070
+ *
2071
+ * This source code is licensed under the MIT license found in the
2072
+ * LICENSE file in the root directory of this source tree.
2073
+ *
2074
+ */
2075
+
2076
+
2077
+ /** @internal */
2078
+
2079
+ /** @internal */
2080
+
2081
+ function mergeSortedAsc(a, b) {
2082
+ const out = [];
2083
+ let i = 0;
2084
+ let j = 0;
2085
+ while (i < a.length && j < b.length) {
2086
+ if (a[i] <= b[j]) {
2087
+ out.push(a[i++]);
2088
+ } else {
2089
+ out.push(b[j++]);
2090
+ }
2091
+ }
2092
+ while (i < a.length) {
2093
+ out.push(a[i++]);
2094
+ }
2095
+ while (j < b.length) {
2096
+ out.push(b[j++]);
2097
+ }
2098
+ return out;
2099
+ }
2100
+
2101
+ /**
2102
+ * Compile an ordered list of {@link DOMImportRule}s into the dispatch tables
2103
+ * used by the import runtime. The rule at index 0 is the highest-priority
2104
+ * (`mergeConfig` prepends partial.rules so later-merged extensions land
2105
+ * first).
2106
+ *
2107
+ * @internal
2108
+ */
2109
+ function compileImportRules(rules) {
2110
+ const compiled = [];
2111
+ const byTag = new Map();
2112
+ const wildcardIndices = [];
2113
+ const textIndices = [];
2114
+ const commentIndices = [];
2115
+ const seenNames = new Set();
2116
+ rules.forEach((rule, i) => {
2117
+ const sel = getSelectorImpl(rule.match);
2118
+ const name = rule.name || defaultRuleName(sel, i);
2119
+ if (typeof rule.name === 'string' && seenNames.has(rule.name)) {
2120
+ console.warn(`[lexical] duplicate DOMImportRule name "${rule.name}" — keep names unique to aid debugging.`);
2121
+ }
2122
+ if (rule.name) {
2123
+ seenNames.add(rule.name);
2124
+ }
2125
+ compiled.push({
2126
+ $import: rule.$import,
2127
+ name,
2128
+ predicate: sel.predicate
2129
+ });
2130
+ if (sel.kind === 'text') {
2131
+ textIndices.push(i);
2132
+ } else if (sel.kind === 'comment') {
2133
+ commentIndices.push(i);
2134
+ } else if (sel.tags.size === 0) {
2135
+ wildcardIndices.push(i);
2136
+ } else {
2137
+ for (const tag of sel.tags) {
2138
+ let list = byTag.get(tag);
2139
+ if (!list) {
2140
+ list = [];
2141
+ byTag.set(tag, list);
2142
+ }
2143
+ list.push(i);
2144
+ }
2145
+ }
2146
+ });
2147
+
2148
+ // Interleave wildcard-element indices into each tag's list in registration
2149
+ // (ascending-index) order, so iterating a tag bucket visits both tag-
2150
+ // specific and wildcard rules in the same priority sequence.
2151
+ const finalByTag = new Map();
2152
+ if (wildcardIndices.length === 0) {
2153
+ for (const [tag, list] of byTag) {
2154
+ finalByTag.set(tag, list);
2155
+ }
2156
+ } else {
2157
+ for (const [tag, list] of byTag) {
2158
+ finalByTag.set(tag, mergeSortedAsc(list, wildcardIndices));
2159
+ }
2160
+ }
2161
+ return {
2162
+ byTag: finalByTag,
2163
+ commentIndices,
2164
+ rules: compiled,
2165
+ textIndices,
2166
+ wildcardIndices
2167
+ };
2168
+ }
2169
+ function defaultRuleName(sel, index) {
2170
+ if (sel.kind === 'text') {
2171
+ return `#text@${index}`;
2172
+ }
2173
+ if (sel.kind === 'comment') {
2174
+ return `#comment@${index}`;
2175
+ }
2176
+ if (sel.tags.size === 0) {
2177
+ return `*@${index}`;
2178
+ }
2179
+ const tagList = Array.from(sel.tags).join(',').toLowerCase();
2180
+ return `${tagList}@${index}`;
2181
+ }
2182
+
2183
+ /**
2184
+ * Look up the (already interleaved) rule indices relevant to `node`. Element
2185
+ * nodes hit `byTag` (with wildcards merged in) or fall back to the wildcard
2186
+ * bucket if no tag-specific rules exist; text and comment nodes use their
2187
+ * own buckets.
2188
+ *
2189
+ * @internal
2190
+ */
2191
+ function getDispatchIndices(dispatch, node) {
2192
+ if (lexical.isDOMTextNode(node)) {
2193
+ return dispatch.textIndices;
2194
+ }
2195
+ if (node.nodeType === 8 /* COMMENT_NODE */) {
2196
+ return dispatch.commentIndices;
2197
+ }
2198
+ if (lexical.isHTMLElement(node)) {
2199
+ return dispatch.byTag.get(node.nodeName) || dispatch.wildcardIndices;
2200
+ }
2201
+ return EMPTY_INDICES;
2202
+ }
2203
+ const EMPTY_INDICES = Object.freeze([]);
2204
+
2205
+ /**
2206
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2207
+ *
2208
+ * This source code is licensed under the MIT license found in the
2209
+ * LICENSE file in the root directory of this source tree.
2210
+ *
2211
+ */
2212
+
2213
+
2214
+ /**
2215
+ * Opaque handle for a pre-compiled set of overlay rules. Produce one with
2216
+ * {@link defineOverlayRules} and pass it to
2217
+ * {@link DOMImportContext.$importChildren} via
2218
+ * {@link ImportChildrenOpts.rules}.
2219
+ *
2220
+ * To merge two or more overlays into a single one, pass them (alongside
2221
+ * raw {@link DOMImportRule}s if desired) to a fresh
2222
+ * {@link defineOverlayRules} — earlier arguments are higher priority.
2223
+ *
2224
+ * The internal shape is intentionally not part of the public API: it's a
2225
+ * compiled dispatch table tagged with `__type` so callers cannot pass a
2226
+ * raw rule array where a compiled overlay is expected.
2227
+ *
2228
+ * @experimental
2229
+ */
2230
+
2231
+ /**
2232
+ * An entry accepted everywhere rules are configured (overlay
2233
+ * definitions, {@link DOMImportConfig.rules}). Either a single
2234
+ * {@link DOMImportRule} or a {@link CompiledOverlayRules} produced by
2235
+ * a previous {@link defineOverlayRules} call — passing the latter
2236
+ * inlines the overlay's rules at this position in priority order.
2237
+ *
2238
+ * @experimental
2239
+ */
2240
+
2241
+ /** @internal */
2242
+ function flattenRuleEntries(entries) {
2243
+ const out = [];
2244
+ for (const entry of entries) {
2245
+ if (isCompiledOverlayRules(entry)) {
2246
+ for (const r of entry.rules) {
2247
+ out.push(r);
2248
+ }
2249
+ } else {
2250
+ out.push(entry);
2251
+ }
2252
+ }
2253
+ return out;
2254
+ }
2255
+ function isCompiledOverlayRules(entry) {
2256
+ return typeof entry === 'object' && entry !== null && '__type' in entry && entry.__type === 'CompiledOverlayRules';
2257
+ }
2258
+
2259
+ /**
2260
+ * Pre-compile a set of {@link DOMImportRuleEntry}s into a
2261
+ * {@link CompiledOverlayRules} handle that can be installed via
2262
+ * `ctx.$importChildren(el, {rules: …})`.
2263
+ *
2264
+ * Entries can be raw {@link DOMImportRule}s or other
2265
+ * {@link CompiledOverlayRules} (the latter are inlined at their
2266
+ * position in priority order, so the same call composes any number of
2267
+ * overlays). Earlier entries are higher priority.
2268
+ *
2269
+ * Overlay rules installed as a raw array would be re-compiled on every
2270
+ * `$importChildren` call. For overlays that are reused (e.g. a GitHub
2271
+ * code-table rule that wraps every matching table), call this once at
2272
+ * module scope so the dispatch table is built up front.
2273
+ *
2274
+ * @experimental
2275
+ */
2276
+ function defineOverlayRules(entries) {
2277
+ const rules = flattenRuleEntries(entries);
2278
+ return {
2279
+ __type: 'CompiledOverlayRules',
2280
+ dispatch: compileImportRules(rules),
2281
+ rules
2282
+ };
2283
+ }
2284
+
2285
+ /**
2286
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2287
+ *
2288
+ * This source code is licensed under the MIT license found in the
2289
+ * LICENSE file in the root directory of this source tree.
2290
+ *
2291
+ */
2292
+
2293
+
2294
+ /**
2295
+ * True if the node fills a block slot at the root or inside another
2296
+ * block — covers both ElementNode-style blocks (paragraph, heading,
2297
+ * quote) and block-level DecoratorNodes (HorizontalRuleNode,
2298
+ * ImageNode-as-block, etc.). Used by {@link BlockSchema},
2299
+ * {@link RootSchema}, and {@link NestedBlockSchema}.
2300
+ *
2301
+ * @experimental
2302
+ */
2303
+ function $isBlockLevel(node) {
2304
+ return lexical.$isBlockElementNode(node) || lexical.$isDecoratorNode(node) && !node.isInline();
2305
+ }
2306
+
2307
+ /**
2308
+ * Distribute an inline wrapper (`LinkNode`, `MarkNode`, …) across a
2309
+ * heterogeneous run of children produced by `$importChildren`, lifting
2310
+ * any block children to the top level while keeping the wrapper around
2311
+ * the leaf inline content.
2312
+ *
2313
+ * Use from a rule whose DOM source is an inline element that the
2314
+ * browser permitted to enclose block elements — the canonical case is
2315
+ * `<a href="…"><h1>title</h1><div>body</div></a>`, which a link rule
2316
+ * wants to surface as two block siblings (heading + paragraph), each
2317
+ * with its own link wrapping the original inline content. Schemas
2318
+ * can't express this because they reason about a parent's children
2319
+ * only — they cannot lift the parent out of itself.
2320
+ *
2321
+ * For each top-level child:
2322
+ * - **Inline children** are collected into runs; each run is wrapped
2323
+ * in a single fresh wrapper (from `$makeWrapper()`).
2324
+ * - **Block children** are descended into: their own children are
2325
+ * recursively distributed with `$makeWrapper`, then re-attached so
2326
+ * the block keeps its position at the top level.
2327
+ *
2328
+ * The returned list will contain a mix of blocks and wrapped inline
2329
+ * runs. The enclosing schema (typically {@link BlockSchema}) will
2330
+ * then package those inline wrappers into paragraphs as usual.
2331
+ *
2332
+ * @experimental
2333
+ */
2334
+ function $distributeInlineWrapper(children, $makeWrapper) {
2335
+ const out = [];
2336
+ let inlineRun = [];
2337
+ const flushInline = () => {
2338
+ if (inlineRun.length === 0) {
2339
+ return;
2340
+ }
2341
+ out.push($makeWrapper().splice(0, 0, inlineRun));
2342
+ inlineRun = [];
2343
+ };
2344
+ for (const child of children) {
2345
+ if ($isBlockLevel(child)) {
2346
+ flushInline();
2347
+ // Recursively distribute the wrapper into the block's own
2348
+ // children. A block DecoratorNode (no children) is left alone.
2349
+ if (lexical.$isElementNode(child)) {
2350
+ const wrapped = $distributeInlineWrapper(child.getChildren(), $makeWrapper);
2351
+ child.splice(0, child.getChildrenSize(), wrapped);
2352
+ }
2353
+ out.push(child);
2354
+ } else {
2355
+ inlineRun.push(child);
2356
+ }
2357
+ }
2358
+ flushInline();
2359
+ return out;
2360
+ }
2361
+
2362
+ /**
2363
+ * Apply a {@link ChildSchema} to a flat list of children produced by
2364
+ * `$importChildren`. Walks the list once, partitions into accepted vs.
2365
+ * rejected runs, packages or drops rejected runs, then runs `$finalize`.
2366
+ *
2367
+ * @internal
2368
+ */
2369
+ function $applySchema(schema, children, parent, domParent) {
2370
+ const out = [];
2371
+ let run = null;
2372
+ const flushRun = () => {
2373
+ if (run === null) {
2374
+ return;
2375
+ }
2376
+ const rejected = run;
2377
+ run = null;
2378
+ if (schema.$packageRun) {
2379
+ const packaged = schema.$packageRun(rejected, parent, domParent);
2380
+ if (packaged.length > 0) {
2381
+ for (const n of packaged) {
2382
+ out.push(n);
2383
+ }
2384
+ return;
2385
+ }
2386
+ }
2387
+ // No $packageRun (or it returned []) — apply onReject. 'drop' (default)
2388
+ // discards the run. 'hoist' lets it through unchanged at this level.
2389
+ if (schema.onReject === 'hoist') {
2390
+ for (const n of rejected) {
2391
+ out.push(n);
2392
+ }
2393
+ }
2394
+ };
2395
+ for (const child of children) {
2396
+ if (schema.$accepts(child, parent)) {
2397
+ flushRun();
2398
+ out.push(child);
2399
+ } else {
2400
+ if (run === null) {
2401
+ run = [];
2402
+ }
2403
+ run.push(child);
2404
+ }
2405
+ }
2406
+ flushRun();
2407
+ return schema.$finalize ? schema.$finalize(out, parent) : out;
2408
+ }
2409
+
2410
+ /**
2411
+ * Wrap a run of inline lexical nodes in a fresh paragraph, propagating the
2412
+ * `text-align` of `domParent` as the paragraph's format type (matching the
2413
+ * legacy `wrapContinuousInlines` behavior).
2414
+ */
2415
+ function $paragraphPackageRun(run, _parent, domParent) {
2416
+ const paragraph = lexical.$createParagraphNode();
2417
+ if (lexical.isHTMLElement(domParent)) {
2418
+ const textAlign = domParent.style.textAlign;
2419
+ if (isAlignmentValue(textAlign)) {
2420
+ paragraph.setFormat(textAlign);
2421
+ }
2422
+ }
2423
+ return [paragraph.splice(0, 0, run)];
2424
+ }
2425
+
2426
+ /**
2427
+ * Default schema for block-level positions (root of the document, the body
2428
+ * of a block element node). Accepts block lexical nodes; packages runs of
2429
+ * inline children into fresh paragraph nodes.
2430
+ *
2431
+ * @experimental
2432
+ */
2433
+ const BlockSchema = {
2434
+ $accepts: $isBlockLevel,
2435
+ $packageRun: $paragraphPackageRun,
2436
+ name: 'BlockSchema'
2437
+ };
2438
+
2439
+ /**
2440
+ * Schema for inline-only positions (the body of an inline lexical node such
2441
+ * as a link). Accepts non-block lexical nodes; runs of block children are
2442
+ * dropped (`onReject: 'drop'` is the default).
2443
+ *
2444
+ * @experimental
2445
+ */
2446
+ const InlineSchema = {
2447
+ $accepts: child => !$isBlockLevel(child),
2448
+ name: 'InlineSchema'
2449
+ };
2450
+
2451
+ /**
2452
+ * Schema for nested block positions — the equivalent of the legacy
2453
+ * `ArtificialNode__DO_NOT_USE` flow used when a block DOM element appears
2454
+ * inside another block lexical ancestor. Accepts block nodes; runs of inline
2455
+ * children are emitted with a line break between consecutive runs (instead
2456
+ * of being wrapped in a paragraph, which would introduce an extra level of
2457
+ * nesting).
2458
+ *
2459
+ * @experimental
2460
+ */
2461
+ const NestedBlockSchema = {
2462
+ $accepts: $isBlockLevel,
2463
+ /**
2464
+ * Pass an inline run through unchanged. Because the schema iterator only
2465
+ * groups *maximal* rejected runs (each separated from the next by an
2466
+ * accepted block child), the legacy "linebreak between adjacent inline
2467
+ * groups" case never arises — adjacent inline siblings are already
2468
+ * coalesced into one run.
2469
+ */
2470
+ $packageRun: run => run,
2471
+ name: 'NestedBlockSchema'
2472
+ };
2473
+
2474
+ /**
2475
+ * Schema for the topmost level of `$generateNodesFromDOM`. Identical to
2476
+ * {@link BlockSchema}; aliased for clarity at the entry point and so it can
2477
+ * be overridden separately in the future (e.g. to synthesize a `ListNode`
2478
+ * around runs of orphan `ListItemNode`s).
2479
+ *
2480
+ * @experimental
2481
+ */
2482
+ const RootSchema = {
2483
+ $accepts: $isBlockLevel,
2484
+ $packageRun: $paragraphPackageRun,
2485
+ name: 'RootSchema'
2486
+ };
2487
+
2488
+ /**
2489
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2490
+ *
2491
+ * This source code is licensed under the MIT license found in the
2492
+ * LICENSE file in the root directory of this source tree.
2493
+ *
2494
+ */
2495
+
2496
+ const NO_CAPTURES = Object.freeze({});
2497
+ function makeContext(runtime, captures) {
2498
+ const ctx = {
2499
+ $importChildren: (parent, opts) => $importChildrenInternal(runtime, parent, opts),
2500
+ $importOne: (node, opts) => $importOneInternal(runtime, node, opts),
2501
+ captures,
2502
+ get(cfg) {
2503
+ return $getImportContextValue(cfg, runtime.editor);
2504
+ },
2505
+ session: runtime.session
2506
+ };
2507
+ return ctx;
2508
+ }
2509
+ function $importChildrenInternal(runtime, parent, opts) {
2510
+ const overlay = opts && opts.rules ? opts.rules.dispatch : undefined;
2511
+ if (overlay) {
2512
+ runtime.overlays.push(overlay);
2513
+ }
2514
+ try {
2515
+ const run = () => $importChildrenRun(runtime, parent, opts);
2516
+ return opts && opts.context ? $withImportContext(opts.context, runtime.editor)(run) : run();
2517
+ } finally {
2518
+ if (overlay) {
2519
+ runtime.overlays.pop();
2520
+ }
2521
+ }
2522
+ }
2523
+ function $importChildrenRun(runtime, parent, opts) {
2524
+ const onChild = opts && opts.$onChild;
2525
+ const collected = [];
2526
+ for (const child of Array.from(parent.childNodes)) {
2527
+ const produced = $importOneInternal(runtime, child, undefined);
2528
+ for (const lex of produced) {
2529
+ const result = onChild ? onChild(lex) : lex;
2530
+ if (result != null) {
2531
+ collected.push(result);
2532
+ }
2533
+ }
2534
+ }
2535
+ const afterApplied = opts && opts.$after ? opts.$after(collected) : collected;
2536
+ const schema = opts && opts.schema;
2537
+ if (!schema) {
2538
+ return afterApplied;
2539
+ }
2540
+ return $applySchema(schema, afterApplied, null, parent);
2541
+ }
2542
+ function $importOneInternal(runtime, node, opts) {
2543
+ const run = () => $dispatch(runtime, node);
2544
+ const out = opts && opts.context ? $withImportContext(opts.context, runtime.editor)(run) : run();
2545
+ // Surface to callers as a mutable array per the DOMImportContext contract.
2546
+ return out;
2547
+ }
2548
+
2549
+ /**
2550
+ * Build the candidate (dispatch, indices) list for `node`. Overlays are
2551
+ * tried first in top-of-stack order; the main dispatcher comes last. The
2552
+ * `$next()` chain walks through all of them in sequence — an overlay rule
2553
+ * can defer to a lower overlay rule, or all the way through to a main
2554
+ * rule, just by calling `$next()`.
2555
+ */
2556
+ function getCandidates(runtime, node) {
2557
+ const candidates = [];
2558
+ for (let i = runtime.overlays.length - 1; i >= 0; i--) {
2559
+ const d = runtime.overlays[i];
2560
+ const idx = getDispatchIndices(d, node);
2561
+ if (idx.length > 0) {
2562
+ candidates.push({
2563
+ dispatch: d,
2564
+ indices: idx
2565
+ });
2566
+ }
2567
+ }
2568
+ const mainIdx = getDispatchIndices(runtime.dispatch, node);
2569
+ if (mainIdx.length > 0) {
2570
+ candidates.push({
2571
+ dispatch: runtime.dispatch,
2572
+ indices: mainIdx
2573
+ });
2574
+ }
2575
+ return candidates;
2576
+ }
2577
+ function $dispatch(runtime, node) {
2578
+ const candidates = getCandidates(runtime, node);
2579
+ if (candidates.length === 0) {
2580
+ return $hoistChildrenOf(runtime, node);
2581
+ }
2582
+ let groupCursor = 0;
2583
+ let ruleCursor = 0;
2584
+ const $next = () => {
2585
+ while (groupCursor < candidates.length) {
2586
+ const {
2587
+ dispatch,
2588
+ indices
2589
+ } = candidates[groupCursor];
2590
+ while (ruleCursor < indices.length) {
2591
+ const idx = indices[ruleCursor++];
2592
+ const rule = dispatch.rules[idx];
2593
+ const captures = {};
2594
+ if (rule.predicate(node, captures)) {
2595
+ const ctx = makeContext(runtime, Object.keys(captures).length === 0 ? NO_CAPTURES : captures);
2596
+ try {
2597
+ return rule.$import(ctx, node, $next);
2598
+ } catch (e) {
2599
+ {
2600
+ console.error(`[lexical] DOM import rule "${rule.name}" threw on node`, node, e);
2601
+ }
2602
+ throw e;
2603
+ }
2604
+ }
2605
+ }
2606
+ groupCursor++;
2607
+ ruleCursor = 0;
2608
+ }
2609
+ return $hoistChildrenOf(runtime, node);
2610
+ };
2611
+ return $next();
2612
+ }
2613
+
2614
+ /**
2615
+ * Fallback when no rule matched and `$next()` was called past the end of the
2616
+ * chain: hoist the element's children to take its place, recursively. Pure
2617
+ * elements with no rule become invisible, matching the legacy
2618
+ * `$createNodesFromDOM` hoisting behavior.
2619
+ */
2620
+ function $hoistChildrenOf(runtime, node) {
2621
+ if (node.childNodes.length === 0) {
2622
+ return [];
2623
+ }
2624
+ const collected = [];
2625
+ for (const child of Array.from(node.childNodes)) {
2626
+ const produced = $importOneInternal(runtime, child, undefined);
2627
+ for (const lex of produced) {
2628
+ collected.push(lex);
2629
+ }
2630
+ }
2631
+ return collected;
2632
+ }
2633
+
2634
+ /**
2635
+ * Top-level walker for a compiled dispatcher. Iterates the DOM children of
2636
+ * `dom` (using the document body if a {@link Document} is passed) and
2637
+ * applies `RootSchema` to the produced lexical nodes so runs of inlines are
2638
+ * wrapped in paragraphs — same shape as the legacy `$generateNodesFromDOM`.
2639
+ *
2640
+ * @internal
2641
+ */
2642
+ function $runImport(dispatch, editor, dom, session) {
2643
+ // Prime the overlay stack with any overlays a preprocess wrote to
2644
+ // ImportOverlays. These remain in effect for the entire walk; nested
2645
+ // `$importChildren({rules})` calls push on top.
2646
+ const installed = session.get(ImportOverlays);
2647
+ const runtime = {
2648
+ dispatch,
2649
+ editor,
2650
+ overlays: installed.map(o => o.dispatch),
2651
+ session
2652
+ };
2653
+ const rootParent = lexical.isDOMDocumentNode(dom) ? dom.body : dom;
2654
+ return $importChildrenRun(runtime, rootParent, {
2655
+ schema: RootSchema
2656
+ });
2657
+ }
2658
+
2659
+ /**
2660
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2661
+ *
2662
+ * This source code is licensed under the MIT license found in the
2663
+ * LICENSE file in the root directory of this source tree.
2664
+ *
2665
+ */
2666
+
2667
+
2668
+ /**
2669
+ * Configuration for {@link DOMImportExtension}.
2670
+ *
2671
+ * @experimental
2672
+ */
2673
+
2674
+ /**
2675
+ * Drive a stack of {@link DOMPreprocessFn}s top-to-bottom: the highest-
2676
+ * index fn runs first and may call `$next()` to defer to the next-lower
2677
+ * one. Matches the export-side `callExportMimeTypeFunctionStack` shape.
2678
+ */
2679
+ function $runPreprocessStack(stack, dom, ctx) {
2680
+ let i = stack.length - 1;
2681
+ const $next = () => {
2682
+ while (i >= 0) {
2683
+ const cur = stack[i--];
2684
+ cur(dom, ctx, $next);
2685
+ return;
2686
+ }
2687
+ };
2688
+ $next();
2689
+ }
2690
+
2691
+ /**
2692
+ * Lowest-priority catch-all rule used as the default `config.rules` entry
2693
+ * for {@link DOMImportExtension}: descends into the element's children
2694
+ * and returns whatever they produced. With no other matching rule, an
2695
+ * element vanishes and its contents are inserted in its place — the
2696
+ * legacy `$createNodesFromDOM` hoisting behavior, but now expressed as a
2697
+ * regular rule that apps can override (e.g. with a `sel.any()` rule that
2698
+ * captures and discards unknown elements).
2699
+ *
2700
+ * @experimental
2701
+ */
2702
+ const DefaultHoistRule = defineImportRule({
2703
+ $import: (ctx, el) => ctx.$importChildren(el),
2704
+ match: selBase.any(),
2705
+ name: '@lexical/html/default-hoist'
2706
+ });
2707
+
2708
+ /**
2709
+ * @experimental
2710
+ *
2711
+ * Extension-based replacement for the legacy `importDOM` / `DOMConversion`
2712
+ * machinery. Rules are contributed via configuration (see
2713
+ * {@link DOMImportConfig.rules}), compiled into a tag-bucketed dispatcher at
2714
+ * editor build time, and consumed via the extension's
2715
+ * {@link DOMImportExtensionOutput.$generateNodesFromDOM} output.
2716
+ *
2717
+ * The legacy `$generateNodesFromDOM` continues to work in parallel; the
2718
+ * intent is to migrate node packages over to this extension incrementally.
2719
+ */
2720
+ const DOMImportExtension = lexical.defineExtension({
2721
+ build(editor, config) {
2722
+ const dispatch = compileImportRules(flattenRuleEntries(config.rules));
2723
+ const defaults = contextFromPairs(config.contextDefaults, undefined);
2724
+ const configPreprocess = config.preprocess;
2725
+ return {
2726
+ $generateNodesFromDOM: (dom, options) => {
2727
+ // The session record IS the root layer of the walk's context.
2728
+ // Start with per-call options.context applied on top of the
2729
+ // editor's contextDefaults, then ensure we have a *fresh*
2730
+ // mutable child (never the shared defaults record) so
2731
+ // session.set writes never leak into the editor's config.
2732
+ const fromOpts = options && options.context ? contextFromPairs(options.context, defaults) : defaults;
2733
+ const sessionRecord = fromOpts !== undefined && fromOpts !== defaults ? fromOpts : Object.create(defaults || null);
2734
+ const session = new ImportSessionImpl(sessionRecord);
2735
+ const preprocessCtx = {
2736
+ session
2737
+ };
2738
+ // Stack of preprocessors: config-level first, then per-call.
2739
+ // Top of stack (last in array) runs first; `next()` defers to
2740
+ // the next-lower one. Matches the GetClipboardDataExtension
2741
+ // convention so app-registered preprocessors can wrap built-in
2742
+ // ones via `next()`. Preprocess writes via `ctx.session.set`
2743
+ // mutate the session record directly.
2744
+ const stack = options && options.preprocess ? [...configPreprocess, ...options.preprocess] : configPreprocess;
2745
+ $runPreprocessStack(stack, dom, preprocessCtx);
2746
+ return $withFullContext(DOMImportContextSymbol, sessionRecord, () => $runImport(dispatch, editor, dom, session), editor);
2747
+ },
2748
+ defaults
2749
+ };
2750
+ },
2751
+ config: {
2752
+ contextDefaults: [],
2753
+ preprocess: [$inlineStylesFromStyleSheets],
2754
+ rules: [DefaultHoistRule]
2755
+ },
2756
+ mergeConfig(config, partial) {
2757
+ return lexical.shallowMergeConfig(config, {
2758
+ ...partial,
2759
+ ...(partial.contextDefaults && {
2760
+ contextDefaults: [...config.contextDefaults, ...partial.contextDefaults]
2761
+ }),
2762
+ ...(partial.preprocess && {
2763
+ preprocess: [...config.preprocess, ...partial.preprocess]
2764
+ }),
2765
+ ...(partial.rules && {
2766
+ rules: [...partial.rules, ...config.rules]
2767
+ })
2768
+ });
2769
+ },
2770
+ name: DOMImportExtensionName
2771
+ });
2772
+
2773
+ /**
2774
+ * Look up the editor's {@link DOMImportExtension} and run its
2775
+ * `$generateNodesFromDOM`. Designed as a drop-in replacement for the
2776
+ * legacy `$generateNodesFromDOM(editor, dom)` signature so it can be
2777
+ * supplied to `ClipboardImportExtension.$generateNodesFromDOM` (or any
2778
+ * other consumer that wants to route through the extension pipeline).
2779
+ *
2780
+ * Throws if the editor was not built with {@link DOMImportExtension} as a
2781
+ * dependency.
2782
+ *
2783
+ * @experimental
2784
+ */
2785
+ function $generateNodesFromDOMViaExtension(dom, options) {
2786
+ return extension.$getExtensionOutput(DOMImportExtension).$generateNodesFromDOM(dom, options);
2787
+ }
2788
+
2789
+ /**
2790
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2791
+ *
2792
+ * This source code is licensed under the MIT license found in the
2793
+ * LICENSE file in the root directory of this source tree.
2794
+ *
2795
+ */
2796
+
2797
+ /**
2798
+ * Bundles {@link CoreImportRules} into a {@link DOMImportExtension}-aware
2799
+ * extension. Depend on this from your editor (directly or via richer
2800
+ * extensions like `RichTextImportExtension`) to get the equivalent of the
2801
+ * legacy core `importDOM` behavior for `<p>`, `<span>`, `<b>`,
2802
+ * `<strong>`, `<em>`, `<i>`, `<code>`, `<mark>`, `<s>`, `<sub>`, `<sup>`,
2803
+ * `<u>`, `<br>`, and `#text`.
2804
+ *
2805
+ * @experimental
2806
+ */
2807
+ const CoreImportExtension = lexical.defineExtension({
2808
+ dependencies: [lexical.configExtension(DOMImportExtension, {
2809
+ rules: CoreImportRules
2810
+ })],
2811
+ name: '@lexical/html/CoreImport'
2812
+ });
2813
+
2814
+ /**
2815
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2816
+ *
2817
+ * This source code is licensed under the MIT license found in the
2818
+ * LICENSE file in the root directory of this source tree.
2819
+ *
2820
+ */
2821
+
2822
+ const HorizontalRuleRule = defineImportRule({
2823
+ $import: () => [extension.$createHorizontalRuleNode()],
2824
+ match: selBase.tag('hr'),
2825
+ name: '@lexical/html/hr'
2826
+ });
2827
+
2828
+ /**
2829
+ * Import rules for {@link HorizontalRuleNode}.
2830
+ *
2831
+ * @experimental
2832
+ */
2833
+ const HorizontalRuleImportRules = [HorizontalRuleRule];
2834
+
2835
+ /**
2836
+ * Bundles {@link HorizontalRuleImportRules} (plus
2837
+ * {@link CoreImportExtension}) into a single dependency. The legacy
2838
+ * {@link HorizontalRuleExtension.importDOM} continues to work in parallel;
2839
+ * depend on this extension to opt into the new pipeline.
2840
+ *
2841
+ * Lives in `@lexical/html` (not `@lexical/extension`) because
2842
+ * {@link DOMImportExtension} itself is in `@lexical/html`, and
2843
+ * `@lexical/extension` is upstream of `@lexical/html` in the dependency
2844
+ * graph — same arrangement as {@link CoreImportExtension}.
2845
+ *
2846
+ * @experimental
2847
+ */
2848
+ const HorizontalRuleImportExtension = lexical.defineExtension({
2849
+ dependencies: [CoreImportExtension, extension.HorizontalRuleExtension, lexical.configExtension(DOMImportExtension, {
2850
+ rules: HorizontalRuleImportRules
2851
+ })],
2852
+ name: '@lexical/html/HorizontalRuleImport'
2853
+ });
2854
+
2855
+ /**
2856
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
2857
+ *
2858
+ * This source code is licensed under the MIT license found in the
2859
+ * LICENSE file in the root directory of this source tree.
2860
+ *
2861
+ */
2862
+
2863
+ /**
2864
+ * Combinator-and-parser-based builder for {@link CompiledSelector}s. The
2865
+ * runtime shape returned by these factory methods is opaque; consumers
2866
+ * should never inspect or construct selector objects directly.
2867
+ *
2868
+ * @experimental
2869
+ */
2870
+ const sel = {
2871
+ any: selBase.any,
2872
+ comment: selBase.comment,
2873
+ /**
2874
+ * Parse a reduced CSS-selector subset and return a builder you can chain
2875
+ * combinator methods off of.
2876
+ */
2877
+ css: parseSelector,
2878
+ tag: selBase.tag,
2879
+ text: selBase.text
2880
+ };
2881
+
2882
+ const IGNORE_TAGS = new Set(['STYLE', 'SCRIPT']);
2883
+
2884
+ /**
2885
+ * How you parse your html string to get a document is left up to you. In the browser you can use the native
2886
+ * DOMParser API to generate a document (see clipboard.ts), but to use in a headless environment you can use JSDom
2887
+ * or an equivalent library and pass in the document here.
2888
+ */
2889
+ function $generateNodesFromDOM(editor, dom) {
2890
+ $inlineStylesFromStyleSheetsDOM(dom);
2891
+ const elements = lexical.isDOMDocumentNode(dom) ? dom.body.childNodes : dom.childNodes;
2892
+ const lexicalNodes = [];
2893
+ const allArtificialNodes = [];
2894
+ for (const element of elements) {
2895
+ if (!IGNORE_TAGS.has(element.nodeName)) {
2896
+ const lexicalNode = $createNodesFromDOM(element, editor, allArtificialNodes, false);
2897
+ if (lexicalNode !== null) {
2898
+ for (const node of lexicalNode) {
2899
+ lexicalNodes.push(node);
2900
+ }
2901
+ }
2902
+ }
2903
+ }
2904
+ $unwrapArtificialNodes(allArtificialNodes);
2905
+ return lexicalNodes;
2906
+ }
2907
+
2908
+ /**
2909
+ * Generate DOM nodes from the editor state into the given container element,
2910
+ * using the editor's {@link EditorDOMRenderConfig}.
2911
+ * @experimental
2912
+ */
2913
+ function $generateDOMFromNodes(container, selection = null, editor = lexical.$getEditor()) {
2914
+ return $withRenderContext([contextValue(RenderContextExport, true)], editor)(() => {
2915
+ const root = lexical.$getRoot();
2916
+ const domConfig = $getSessionDOMRenderConfig(editor);
2917
+ const parentElementAppend = container.append.bind(container);
2918
+ for (const topLevelNode of root.getChildren()) {
2919
+ $appendNodesToHTML(editor, topLevelNode, parentElementAppend, selection, domConfig);
2920
+ }
2921
+ return container;
2922
+ });
2923
+ }
2924
+
2925
+ /**
2926
+ * Generate DOM nodes from a root node into the given container element,
2927
+ * including the root node itself. Uses the editor's {@link EditorDOMRenderConfig}.
2928
+ * @experimental
2929
+ */
2930
+ function $generateDOMFromRoot(container, root = lexical.$getRoot()) {
2931
+ const editor = lexical.$getEditor();
2932
+ return $withRenderContext([contextValue(RenderContextExport, true), contextValue(RenderContextRoot, true)], editor)(() => {
2933
+ const selection = null;
2934
+ const domConfig = $getSessionDOMRenderConfig(editor);
2935
+ const parentElementAppend = container.append.bind(container);
2936
+ $appendNodesToHTML(editor, root, parentElementAppend, selection, domConfig);
2937
+ return container;
2938
+ });
2939
+ }
2940
+
2941
+ /**
2942
+ * Generate an HTML string from the editor's current state (or `selection`
2943
+ * if provided).
2944
+ *
2945
+ * Must be called inside an active editor scope — i.e. `editor.update(...)`,
2946
+ * `editor.read(...)`, or `editor.getEditorState().read(callback, {editor})`.
2947
+ * The legacy `editor.getEditorState().read(callback)` call (without the
2948
+ * `{editor}` option) does not set an active editor and is not supported;
2949
+ * `editor.read(...)` is the drop-in replacement.
2950
+ */
2951
+ function $generateHtmlFromNodes(editor, selection = null) {
2952
+ if (typeof document === 'undefined' || typeof window === 'undefined' && typeof global.window === 'undefined') {
2953
+ {
2954
+ 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.`);
2955
+ }
2956
+ }
2957
+ return $generateDOMFromNodes(document.createElement('div'), selection, editor).innerHTML;
2958
+ }
2959
+ function $appendNodesToHTML(editor, currentNode, parentElementAppend, selection$1 = null, domConfig = lexical.$getEditorDOMRenderConfig(editor)) {
2960
+ let shouldInclude = domConfig.$shouldInclude(currentNode, selection$1, editor);
2961
+ const shouldExclude = domConfig.$shouldExclude(currentNode, selection$1, editor);
2962
+ let target = currentNode;
2963
+ if (selection$1 !== null && lexical.$isTextNode(currentNode)) {
2964
+ target = selection.$sliceSelectedTextNodeContent(selection$1, currentNode, 'clone');
2965
+ }
2966
+ const exportProps = domConfig.$exportDOM(target, editor);
2967
+ const {
2968
+ element,
2969
+ after,
2970
+ append,
2971
+ $getChildNodes
2972
+ } = exportProps;
2973
+ if (!element) {
2974
+ return false;
2975
+ }
2976
+ const fragment = document.createDocumentFragment();
2977
+ const children = $getChildNodes ? $getChildNodes() : lexical.$isElementNode(target) ? target.getChildren() : [];
2978
+ const fragmentAppend = fragment.append.bind(fragment);
2979
+ for (const childNode of children) {
2980
+ const shouldIncludeChild = $appendNodesToHTML(editor, childNode, fragmentAppend, selection$1, domConfig);
2981
+ if (!shouldInclude && shouldIncludeChild && domConfig.$extractWithChild(currentNode, childNode, selection$1, 'html', editor)) {
2982
+ shouldInclude = true;
2983
+ }
2984
+ }
2985
+ if (shouldInclude && !shouldExclude) {
2986
+ if (lexical.isHTMLElement(element) || lexical.isDocumentFragment(element)) {
2987
+ if (append) {
2988
+ append(fragment);
2989
+ } else {
2990
+ element.append(fragment);
2991
+ }
2992
+ }
2993
+ parentElementAppend(element);
2994
+ if (after) {
2995
+ const newElement = after.call(target, element);
2996
+ if (newElement) {
2997
+ if (lexical.isDocumentFragment(element)) {
2998
+ element.replaceChildren(newElement);
2999
+ } else {
3000
+ element.replaceWith(newElement);
3001
+ }
3002
+ }
3003
+ }
3004
+ } else {
3005
+ parentElementAppend(fragment);
3006
+ }
3007
+ return shouldInclude;
3008
+ }
3009
+ function getConversionFunction(domNode, editor) {
3010
+ const {
3011
+ nodeName
3012
+ } = domNode;
3013
+ const cachedConversions = editor._htmlConversions.get(nodeName.toLowerCase());
3014
+ let currentConversion = null;
3015
+ if (cachedConversions !== undefined) {
3016
+ for (const cachedConversion of cachedConversions) {
3017
+ const domConversion = cachedConversion(domNode);
3018
+ if (domConversion !== null && (currentConversion === null ||
3019
+ // Given equal priority, prefer the last registered importer
3020
+ // which is typically an application custom node or HTMLConfig['import']
3021
+ (currentConversion.priority || 0) <= (domConversion.priority || 0))) {
3022
+ currentConversion = domConversion;
3023
+ }
3024
+ }
3025
+ }
3026
+ return currentConversion !== null ? currentConversion.conversion : null;
3027
+ }
3028
+ function $createNodesFromDOM(node, editor, allArtificialNodes, hasBlockAncestorLexicalNode, forChildMap = new Map(), parentLexicalNode) {
3029
+ const lexicalNodes = [];
3030
+ if (IGNORE_TAGS.has(node.nodeName)) {
3031
+ return lexicalNodes;
3032
+ }
3033
+ let currentLexicalNode = null;
3034
+ const transformFunction = getConversionFunction(node, editor);
3035
+ const transformOutput = transformFunction ? transformFunction(node) : null;
3036
+ let postTransform = null;
3037
+ if (transformOutput !== null) {
3038
+ postTransform = transformOutput.after;
3039
+ const transformNodes = transformOutput.node;
3040
+ currentLexicalNode = Array.isArray(transformNodes) ? transformNodes[transformNodes.length - 1] : transformNodes;
3041
+ if (currentLexicalNode !== null) {
3042
+ for (const [, forChildFunction] of forChildMap) {
3043
+ currentLexicalNode = forChildFunction(currentLexicalNode, parentLexicalNode);
3044
+ if (!currentLexicalNode) {
3045
+ break;
3046
+ }
3047
+ }
3048
+ if (currentLexicalNode) {
3049
+ lexicalNodes.push(...(Array.isArray(transformNodes) ? transformNodes : [currentLexicalNode]));
3050
+ }
3051
+ }
3052
+ if (transformOutput.forChild != null) {
3053
+ forChildMap.set(node.nodeName, transformOutput.forChild);
3054
+ }
3055
+ }
3056
+
3057
+ // If the DOM node doesn't have a transformer, we don't know what
3058
+ // to do with it but we still need to process any childNodes.
3059
+ const children = node.childNodes;
3060
+ let childLexicalNodes = [];
3061
+ const hasBlockAncestorLexicalNodeForChildren = currentLexicalNode != null && lexical.$isRootOrShadowRoot(currentLexicalNode) ? false : currentLexicalNode != null && lexical.$isBlockElementNode(currentLexicalNode) || hasBlockAncestorLexicalNode;
3062
+ for (let i = 0; i < children.length; i++) {
3063
+ childLexicalNodes.push(...$createNodesFromDOM(children[i], editor, allArtificialNodes, hasBlockAncestorLexicalNodeForChildren, new Map(forChildMap), currentLexicalNode));
3064
+ }
3065
+ if (postTransform != null) {
3066
+ childLexicalNodes = postTransform(childLexicalNodes);
3067
+ }
3068
+ if (lexical.isBlockDomNode(node)) {
3069
+ if (!hasBlockAncestorLexicalNodeForChildren) {
3070
+ childLexicalNodes = wrapContinuousInlines(node, childLexicalNodes, lexical.$createParagraphNode);
3071
+ } else {
3072
+ childLexicalNodes = wrapContinuousInlines(node, childLexicalNodes, () => {
3073
+ const artificialNode = new lexical.ArtificialNode__DO_NOT_USE();
3074
+ allArtificialNodes.push(artificialNode);
3075
+ return artificialNode;
3076
+ });
3077
+ }
3078
+ }
3079
+ if (currentLexicalNode == null) {
3080
+ if (childLexicalNodes.length > 0) {
3081
+ // If it hasn't been converted to a LexicalNode, we hoist its children
3082
+ // up to the same level as it.
3083
+ for (const childNode of childLexicalNodes) {
3084
+ lexicalNodes.push(childNode);
3085
+ }
3086
+ } else {
3087
+ if (lexical.isBlockDomNode(node) && isDomNodeBetweenTwoInlineNodes(node)) {
3088
+ // Empty block dom node that hasnt been converted, we replace it with a linebreak if its between inline nodes
3089
+ lexicalNodes.push(lexical.$createLineBreakNode());
3090
+ }
3091
+ }
3092
+ } else {
3093
+ if (lexical.$isElementNode(currentLexicalNode)) {
3094
+ // If the current node is a ElementNode after conversion,
3095
+ // we can append all the children to it.
3096
+ currentLexicalNode.append(...childLexicalNodes);
3097
+ }
3098
+ }
3099
+ return lexicalNodes;
3100
+ }
3101
+ function wrapContinuousInlines(domNode, nodes, createWrapperFn) {
3102
+ const textAlign = domNode.style.textAlign;
3103
+ const out = [];
3104
+ let continuousInlines = [];
3105
+ // wrap contiguous inline child nodes in para
3106
+ for (let i = 0; i < nodes.length; i++) {
3107
+ const node = nodes[i];
3108
+ if (lexical.$isBlockElementNode(node)) {
3109
+ if (textAlign && !node.getFormat()) {
3110
+ node.setFormat(textAlign);
3111
+ }
3112
+ out.push(node);
3113
+ } else {
3114
+ continuousInlines.push(node);
3115
+ if (i === nodes.length - 1 || i < nodes.length - 1 && lexical.$isBlockElementNode(nodes[i + 1])) {
3116
+ const wrapper = createWrapperFn();
3117
+ wrapper.setFormat(textAlign);
3118
+ wrapper.append(...continuousInlines);
3119
+ out.push(wrapper);
3120
+ continuousInlines = [];
3121
+ }
3122
+ }
3123
+ }
3124
+ return out;
3125
+ }
3126
+ function $unwrapArtificialNodes(allArtificialNodes) {
3127
+ // Replace artificial node with its children, inserting a linebreak
3128
+ // between adjacent artificial nodes
3129
+ for (const node of allArtificialNodes) {
3130
+ if (node.getParent() && node.getNextSibling() instanceof lexical.ArtificialNode__DO_NOT_USE) {
3131
+ node.insertAfter(lexical.$createLineBreakNode());
3132
+ }
3133
+ }
3134
+ for (const node of allArtificialNodes) {
3135
+ const parent = node.getParent();
3136
+ if (parent) {
3137
+ parent.splice(node.getIndexWithinParent(), 1, node.getChildren());
3138
+ }
3139
+ }
3140
+ }
3141
+ function isDomNodeBetweenTwoInlineNodes(node) {
3142
+ if (node.nextSibling == null || node.previousSibling == null) {
3143
+ return false;
3144
+ }
3145
+ return lexical.isInlineDomNode(node.nextSibling) && lexical.isInlineDomNode(node.previousSibling);
3146
+ }
3147
+
3148
+ exports.$distributeInlineWrapper = $distributeInlineWrapper;
3149
+ exports.$generateDOMFromNodes = $generateDOMFromNodes;
3150
+ exports.$generateDOMFromRoot = $generateDOMFromRoot;
3151
+ exports.$generateHtmlFromNodes = $generateHtmlFromNodes;
3152
+ exports.$generateNodesFromDOM = $generateNodesFromDOM;
3153
+ exports.$generateNodesFromDOMViaExtension = $generateNodesFromDOMViaExtension;
3154
+ exports.$getImportContextValue = $getImportContextValue;
3155
+ exports.$getRenderContextValue = $getRenderContextValue;
3156
+ exports.$getSessionDOMRenderConfig = $getSessionDOMRenderConfig;
3157
+ exports.$inlineStylesFromStyleSheets = $inlineStylesFromStyleSheets;
3158
+ exports.$isBlockLevel = $isBlockLevel;
3159
+ exports.$setRenderContextValue = $setRenderContextValue;
3160
+ exports.$updateRenderContextValue = $updateRenderContextValue;
3161
+ exports.$withImportContext = $withImportContext;
3162
+ exports.$withRenderContext = $withRenderContext;
3163
+ exports.BlockSchema = BlockSchema;
3164
+ exports.CoreImportExtension = CoreImportExtension;
3165
+ exports.CoreImportRules = CoreImportRules;
3166
+ exports.DOMImportExtension = DOMImportExtension;
3167
+ exports.DOMRenderExtension = DOMRenderExtension;
3168
+ exports.HorizontalRuleImportExtension = HorizontalRuleImportExtension;
3169
+ exports.HorizontalRuleImportRules = HorizontalRuleImportRules;
3170
+ exports.ImportOverlays = ImportOverlays;
3171
+ exports.ImportSource = ImportSource;
3172
+ exports.ImportSourceDataTransfer = ImportSourceDataTransfer;
3173
+ exports.ImportTextFormat = ImportTextFormat;
3174
+ exports.ImportTextStyle = ImportTextStyle;
3175
+ exports.ImportWhitespaceConfig = ImportWhitespaceConfig;
3176
+ exports.InlineSchema = InlineSchema;
3177
+ exports.NestedBlockSchema = NestedBlockSchema;
3178
+ exports.RenderContextExport = RenderContextExport;
3179
+ exports.RenderContextRoot = RenderContextRoot;
3180
+ exports.RootSchema = RootSchema;
3181
+ exports.contextUpdater = contextUpdater;
3182
+ exports.contextValue = contextValue;
3183
+ exports.createImportState = createImportState;
3184
+ exports.createRenderState = createRenderState;
3185
+ exports.defaultIsInline = defaultIsInline;
3186
+ exports.defaultPreservesWhitespace = defaultPreservesWhitespace;
3187
+ exports.defineImportRule = defineImportRule;
3188
+ exports.defineOverlayRules = defineOverlayRules;
3189
+ exports.domOverride = domOverride;
3190
+ exports.isElementOfTag = isElementOfTag;
3191
+ exports.parseSelector = parseSelector;
3192
+ exports.sel = sel;