@lexical/link 0.50.1-nightly.20260916.0 → 0.51.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,7 +9,7 @@
9
9
  import { AriaLiveRegionExtension } from '@lexical/a11y';
10
10
  import { effect } from '@lexical/extension/signals';
11
11
  import { namedSignals } from '@lexical/extension/namedSignals';
12
- import { ElementNode, $getDocument, addClassNamesToElement, isHTMLAnchorElement, $copyNode, $isRangeSelection, $applyNodeReplacement, createCommand, $getSelection, $isNodeSelection, $findMatchingParent, $normalizeSelection__EXPERIMENTAL, $setSelection, $getChildCaret, $isElementNode, $insertNodeToNearestRootAtCaret, $rewindSiblingCaret, $caretFromPoint, $normalizeCaret, $isSiblingCaret, $setPointFromCaret, shallowMergeConfig, mergeRegister, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, $isTextNode, COMMAND_PRIORITY_LOW, registerEventListeners, isDOMNode, getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, TextNode, $isLineBreakNode, $createTextNode } from 'lexical';
12
+ import { getterTableOf, setterTableOf, setterDefaultOf, ElementNode, $getDocument, addClassNamesToElement, isHTMLAnchorElement, $copyNode, $isRangeSelection, $applyNodeReplacement, createCommand, $getSelection, $isNodeSelection, $findMatchingParent, nodeSchema, withField, stringValue, nullable, booleanValue, $normalizeSelection__EXPERIMENTAL, $setSelection, $getChildCaret, $isElementNode, $insertNodeToNearestRootAtCaret, $rewindSiblingCaret, $caretFromPoint, $normalizeCaret, $isSiblingCaret, $setPointFromCaret, shallowMergeConfig, mergeRegister, COMMAND_PRIORITY_EDITOR, PASTE_COMMAND, $isTextNode, COMMAND_PRIORITY_LOW, registerEventListeners, isDOMNode, getNearestEditorFromDOMNode, $getNearestNodeFromDOMNode, TextNode, $isLineBreakNode, $createTextNode } from 'lexical';
13
13
  import { sel, $distributeInlineWrapper, CoreImportExtension, DOMImportExtension } from '@lexical/html';
14
14
  import { objectKlassEquals } from '@lexical/utils';
15
15
 
@@ -27,9 +27,301 @@ function formatDevErrorMessage(message) {
27
27
  throw new Error(message);
28
28
  }
29
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
+
39
+ // The JSON number grammar, anchored, matching numberValue: `Number()` alone
40
+ // reads '0x10' as 16 and '' as 0, and neither is a shape a JSON encoder
41
+ // produces. Emitted from the same source the codegen verified against, so the
42
+ // two cannot be different functions.
43
+ const JSON_NUMBER = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
44
+ function num(v, d) {
45
+ if (typeof v === 'number') {
46
+ return Number.isFinite(v) ? v : d;
47
+ }
48
+ if (typeof v !== 'string' || !JSON_NUMBER.test(v)) {
49
+ return d;
50
+ }
51
+ const n = Number(v);
52
+ return Number.isFinite(n) ? n : d;
53
+ }
54
+ function numC(v, d, min, max, integer) {
55
+ const n = num(v, d);
56
+ return n >= min && n <= max && (false || Number.isInteger(n)) ? n : d;
57
+ }
58
+
59
+ /**
60
+ * LinkNode's schema-declared fields, for a clone. Generated from that
61
+ * schema; do not edit by hand.
62
+ *
63
+ * @internal
64
+ */
65
+ function afterCloneLinkNode(node, prevNode) {
66
+ node.__rel = prevNode.__rel;
67
+ node.__target = prevNode.__target;
68
+ node.__title = prevNode.__title;
69
+ node.__url = prevNode.__url;
70
+ }
71
+
72
+ /** LinkNode's generated implementations, for its `$config`. @internal */
73
+ const GENERATED_LINK = fields => {
74
+ const LINK_FORMAT_GETTER = getterTableOf(fields, 'format');
75
+ const LINK_FORMAT_SETTER = setterTableOf(fields, 'format');
76
+ const LINK_FORMAT_SETTER_DEFAULT = setterDefaultOf(fields, 'format');
77
+
78
+ /** Generated from LinkNode's serialization schema. Do not edit by hand. */
79
+ function exportLinkNode(node) {
80
+ const textFormat = node.__textFormat;
81
+ const textStyle = node.__textStyle;
82
+ const shouldSerializeTextStyles = (textFormat !== 0 || textStyle !== '') && node.shouldSerializeTextStyles();
83
+ return {
84
+ children: [],
85
+ rel: node.__rel,
86
+ target: node.__target,
87
+ title: node.__title,
88
+ url: node.__url,
89
+ direction: node.__dir,
90
+ format: LINK_FORMAT_GETTER[node.__format],
91
+ indent: node.__indent,
92
+ textFormat: textFormat !== 0 && shouldSerializeTextStyles ? textFormat : undefined,
93
+ textStyle: textStyle !== '' && shouldSerializeTextStyles ? textStyle : undefined,
94
+ type: node.__type,
95
+ version: 1
96
+ };
97
+ }
98
+
99
+ /** Generated from LinkNode's serialization schema. Do not edit by hand. */
100
+ function exportCompactLinkNode(node) {
101
+ const textFormat = node.__textFormat;
102
+ const textStyle = node.__textStyle;
103
+ const shouldSerializeTextStyles = (textFormat !== 0 || textStyle !== '') && node.shouldSerializeTextStyles();
104
+ const json = {
105
+ type: node.__type,
106
+ children: []
107
+ };
108
+ const rel = node.__rel;
109
+ if (rel != null) {
110
+ json.rel = rel;
111
+ }
112
+ const target = node.__target;
113
+ if (target != null) {
114
+ json.target = target;
115
+ }
116
+ const title = node.__title;
117
+ if (title != null) {
118
+ json.title = title;
119
+ }
120
+ const url = node.__url;
121
+ if (url !== undefined && url !== '') {
122
+ json.url = url;
123
+ }
124
+ const direction = node.__dir;
125
+ if (direction != null) {
126
+ json.direction = direction;
127
+ }
128
+ const format = LINK_FORMAT_GETTER[node.__format];
129
+ if (format !== undefined && format !== '') {
130
+ json.format = format;
131
+ }
132
+ const indent = node.__indent;
133
+ if (indent !== undefined && indent !== 0) {
134
+ json.indent = indent;
135
+ }
136
+ if (textFormat !== undefined && textFormat !== 0 && shouldSerializeTextStyles) {
137
+ json.textFormat = textFormat;
138
+ }
139
+ if (textStyle !== undefined && textStyle !== '' && shouldSerializeTextStyles) {
140
+ json.textStyle = textStyle;
141
+ }
142
+ return json;
143
+ }
144
+
145
+ /** Generated from LinkNode's serialization schema. Do not edit by hand. */
146
+ function updateLinkNode(node, json) {
147
+ const direction = json.direction;
148
+ node.__dir = direction === null || direction === 'ltr' || direction === 'rtl' ? direction : null;
149
+ const format = json.format;
150
+ node.__format = typeof format === 'string' && format in LINK_FORMAT_SETTER ? LINK_FORMAT_SETTER[format] : LINK_FORMAT_SETTER_DEFAULT;
151
+ node.__indent = numC(json.indent, 0, 0, Infinity);
152
+ node.__textFormat = num(json.textFormat, 0);
153
+ const textStyle = json.textStyle;
154
+ node.__textStyle = typeof textStyle === 'string' ? textStyle : '';
155
+ const rel = json.rel;
156
+ node.__rel = typeof rel === 'string' && rel !== '' ? rel : null;
157
+ const target = json.target;
158
+ node.__target = typeof target === 'string' && target !== '' ? target : null;
159
+ const title = json.title;
160
+ node.__title = typeof title === 'string' && title !== '' ? title : null;
161
+ const url = json.url;
162
+ node.__url = typeof url === 'string' ? url : '';
163
+ return node;
164
+ }
165
+ return {
166
+ exportJSON: exportLinkNode,
167
+ exportCompactJSON: exportCompactLinkNode,
168
+ updateFromJSON: updateLinkNode,
169
+ afterCloneFrom: afterCloneLinkNode
170
+ };
171
+ };
172
+
173
+ /**
174
+ * AutoLinkNode's schema-declared fields, for a clone. Generated from that
175
+ * schema; do not edit by hand.
176
+ *
177
+ * @internal
178
+ */
179
+ function afterCloneAutoLinkNode(node, prevNode) {
180
+ node.__isUnlinked = prevNode.__isUnlinked;
181
+ }
182
+
183
+ /** AutoLinkNode's generated implementations, for its `$config`. @internal */
184
+ const GENERATED_AUTOLINK = fields => {
185
+ const AUTOLINK_FORMAT_GETTER = getterTableOf(fields, 'format');
186
+ const AUTOLINK_FORMAT_SETTER = setterTableOf(fields, 'format');
187
+ const AUTOLINK_FORMAT_SETTER_DEFAULT = setterDefaultOf(fields, 'format');
188
+
189
+ /** Generated from AutoLinkNode's serialization schema. Do not edit by hand. */
190
+ function exportAutoLinkNode(node) {
191
+ const textFormat = node.__textFormat;
192
+ const textStyle = node.__textStyle;
193
+ const shouldSerializeTextStyles = (textFormat !== 0 || textStyle !== '') && node.shouldSerializeTextStyles();
194
+ return {
195
+ children: [],
196
+ isUnlinked: node.__isUnlinked,
197
+ rel: node.__rel,
198
+ target: node.__target,
199
+ title: node.__title,
200
+ url: node.__url,
201
+ direction: node.__dir,
202
+ format: AUTOLINK_FORMAT_GETTER[node.__format],
203
+ indent: node.__indent,
204
+ textFormat: textFormat !== 0 && shouldSerializeTextStyles ? textFormat : undefined,
205
+ textStyle: textStyle !== '' && shouldSerializeTextStyles ? textStyle : undefined,
206
+ type: node.__type,
207
+ version: 1
208
+ };
209
+ }
210
+
211
+ /** Generated from AutoLinkNode's serialization schema. Do not edit by hand. */
212
+ function exportCompactAutoLinkNode(node) {
213
+ const textFormat = node.__textFormat;
214
+ const textStyle = node.__textStyle;
215
+ const shouldSerializeTextStyles = (textFormat !== 0 || textStyle !== '') && node.shouldSerializeTextStyles();
216
+ const json = {
217
+ type: node.__type,
218
+ children: []
219
+ };
220
+ const isUnlinked = node.__isUnlinked;
221
+ if (isUnlinked !== undefined && isUnlinked !== false) {
222
+ json.isUnlinked = isUnlinked;
223
+ }
224
+ const rel = node.__rel;
225
+ if (rel != null) {
226
+ json.rel = rel;
227
+ }
228
+ const target = node.__target;
229
+ if (target != null) {
230
+ json.target = target;
231
+ }
232
+ const title = node.__title;
233
+ if (title != null) {
234
+ json.title = title;
235
+ }
236
+ const url = node.__url;
237
+ if (url !== undefined && url !== '') {
238
+ json.url = url;
239
+ }
240
+ const direction = node.__dir;
241
+ if (direction != null) {
242
+ json.direction = direction;
243
+ }
244
+ const format = AUTOLINK_FORMAT_GETTER[node.__format];
245
+ if (format !== undefined && format !== '') {
246
+ json.format = format;
247
+ }
248
+ const indent = node.__indent;
249
+ if (indent !== undefined && indent !== 0) {
250
+ json.indent = indent;
251
+ }
252
+ if (textFormat !== undefined && textFormat !== 0 && shouldSerializeTextStyles) {
253
+ json.textFormat = textFormat;
254
+ }
255
+ if (textStyle !== undefined && textStyle !== '' && shouldSerializeTextStyles) {
256
+ json.textStyle = textStyle;
257
+ }
258
+ return json;
259
+ }
260
+
261
+ /** Generated from AutoLinkNode's serialization schema. Do not edit by hand. */
262
+ function updateAutoLinkNode(node, json) {
263
+ const direction = json.direction;
264
+ node.__dir = direction === null || direction === 'ltr' || direction === 'rtl' ? direction : null;
265
+ const format = json.format;
266
+ node.__format = typeof format === 'string' && format in AUTOLINK_FORMAT_SETTER ? AUTOLINK_FORMAT_SETTER[format] : AUTOLINK_FORMAT_SETTER_DEFAULT;
267
+ node.__indent = numC(json.indent, 0, 0, Infinity);
268
+ node.__textFormat = num(json.textFormat, 0);
269
+ const textStyle = json.textStyle;
270
+ node.__textStyle = typeof textStyle === 'string' ? textStyle : '';
271
+ const rel = json.rel;
272
+ node.__rel = typeof rel === 'string' && rel !== '' ? rel : null;
273
+ const target = json.target;
274
+ node.__target = typeof target === 'string' && target !== '' ? target : null;
275
+ const title = json.title;
276
+ node.__title = typeof title === 'string' && title !== '' ? title : null;
277
+ const url = json.url;
278
+ node.__url = typeof url === 'string' ? url : '';
279
+ const isUnlinked = json.isUnlinked;
280
+ node.__isUnlinked = typeof isUnlinked === 'boolean' ? isUnlinked : false;
281
+ return node;
282
+ }
283
+ return {
284
+ exportJSON: exportAutoLinkNode,
285
+ exportCompactJSON: exportCompactAutoLinkNode,
286
+ updateFromJSON: updateAutoLinkNode,
287
+ afterCloneFrom: afterCloneAutoLinkNode
288
+ };
289
+ };
290
+
30
291
  const SUPPORTED_URL_PROTOCOLS = new Set(['http:', 'https:', 'mailto:', 'sms:', 'tel:']);
292
+ const linkNodeSchema = /* @__PURE__ */nodeSchema()({
293
+ // defaultAsNull preserves the legacy `value || null` semantics: an empty
294
+ // string (or junk that coerces to it) imports as null, not ''.
295
+ // Every property *is* its node field in both directions — each accessor is
296
+ // a bare read or write — so each is declared as the field it is. Only `url`
297
+ // names its accessors, because getURL/setURL are not the conventional
298
+ // getUrl/setUrl the others are resolved to.
299
+ rel: /* @__PURE__ */withField(/* @__PURE__ */nullable(/* @__PURE__ */stringValue(), {
300
+ defaultAsNull: true
301
+ }), {
302
+ field: '__rel'
303
+ }),
304
+ target: /* @__PURE__ */withField(/* @__PURE__ */nullable(/* @__PURE__ */stringValue(), {
305
+ defaultAsNull: true
306
+ }), {
307
+ field: '__target'
308
+ }),
309
+ title: /* @__PURE__ */withField(/* @__PURE__ */nullable(/* @__PURE__ */stringValue(), {
310
+ defaultAsNull: true
311
+ }), {
312
+ field: '__title'
313
+ }),
314
+ url: /* @__PURE__ */withField(/* @__PURE__ */stringValue(), {
315
+ field: '__url',
316
+ getter: 'getURL',
317
+ setter: 'setURL'
318
+ })
319
+ });
320
+
321
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
31
322
 
32
323
  /** @noInheritDoc */
324
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
33
325
  class LinkNode extends ElementNode {
34
326
  /** @internal */
35
327
  __url;
@@ -42,12 +334,14 @@ class LinkNode extends ElementNode {
42
334
  $config() {
43
335
  return this.config('link', {
44
336
  extends: ElementNode,
337
+ generated: GENERATED_LINK,
45
338
  importDOM: {
46
339
  a: () => ({
47
340
  conversion: $convertAnchorElement,
48
341
  priority: 1
49
342
  })
50
- }
343
+ },
344
+ json: linkNodeSchema
51
345
  });
52
346
  }
53
347
  constructor(url = '', attributes = {}, key) {
@@ -62,13 +356,6 @@ class LinkNode extends ElementNode {
62
356
  this.__rel = rel;
63
357
  this.__title = title;
64
358
  }
65
- afterCloneFrom(prevNode) {
66
- super.afterCloneFrom(prevNode);
67
- this.__url = prevNode.__url;
68
- this.__rel = prevNode.__rel;
69
- this.__target = prevNode.__target;
70
- this.__title = prevNode.__title;
71
- }
72
359
  createDOM(config) {
73
360
  const element = $getDocument().createElement('a');
74
361
  this.updateLinkDOM(null, element, config);
@@ -97,9 +384,6 @@ class LinkNode extends ElementNode {
97
384
  this.updateLinkDOM(prevNode, anchor, config);
98
385
  return false;
99
386
  }
100
- updateFromJSON(serializedNode) {
101
- return super.updateFromJSON(serializedNode).setURL(serializedNode.url).setRel(serializedNode.rel || null).setTarget(serializedNode.target || null).setTitle(serializedNode.title || null);
102
- }
103
387
  sanitizeUrl(url) {
104
388
  const rawUrl = url;
105
389
  url = formatUrl(url);
@@ -140,15 +424,6 @@ class LinkNode extends ElementNode {
140
424
  }
141
425
  return url;
142
426
  }
143
- exportJSON() {
144
- return {
145
- ...super.exportJSON(),
146
- rel: this.getRel(),
147
- target: this.getTarget(),
148
- title: this.getTitle(),
149
- url: this.getURL()
150
- };
151
- }
152
427
  getURL() {
153
428
  return this.getLatest().__url;
154
429
  }
@@ -354,8 +629,21 @@ function $createLinkNode(url = '', attributes) {
354
629
  function $isLinkNode(node) {
355
630
  return node instanceof LinkNode;
356
631
  }
632
+ const autoLinkNodeSchema = /* @__PURE__ */nodeSchema()({
633
+ // The property *is* the field in both directions — both accessors are bare —
634
+ // so it is declared as the field it is. The conventional
635
+ // getIsUnlinked/setIsUnlinked are still deferred to, so a subclass that
636
+ // overrides either still decides.
637
+ isUnlinked: /* @__PURE__ */withField(/* @__PURE__ */booleanValue(), {
638
+ field: '__isUnlinked'
639
+ })
640
+ });
641
+
642
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
643
+
357
644
  // Custom node type to override `canInsertTextAfter` that will
358
645
  // allow typing within the link
646
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
359
647
  class AutoLinkNode extends LinkNode {
360
648
  /** @internal */
361
649
  /** Indicates whether the autolink was ever unlinked. **/
@@ -364,13 +652,11 @@ class AutoLinkNode extends LinkNode {
364
652
  super(url, attributes, key);
365
653
  this.__isUnlinked = attributes.isUnlinked !== undefined && attributes.isUnlinked !== null ? attributes.isUnlinked : false;
366
654
  }
367
- afterCloneFrom(prevNode) {
368
- super.afterCloneFrom(prevNode);
369
- this.__isUnlinked = prevNode.__isUnlinked;
370
- }
371
655
  $config() {
372
656
  return this.config('autolink', {
373
- extends: LinkNode
657
+ extends: LinkNode,
658
+ generated: GENERATED_AUTOLINK,
659
+ json: autoLinkNodeSchema
374
660
  });
375
661
  }
376
662
  shouldMergeAdjacentLink(_otherLink) {
@@ -394,15 +680,6 @@ class AutoLinkNode extends LinkNode {
394
680
  updateDOM(prevNode, anchor, config) {
395
681
  return super.updateDOM(prevNode, anchor, config) || prevNode.__isUnlinked !== this.__isUnlinked;
396
682
  }
397
- updateFromJSON(serializedNode) {
398
- return super.updateFromJSON(serializedNode).setIsUnlinked(serializedNode.isUnlinked || false);
399
- }
400
- exportJSON() {
401
- return {
402
- ...super.exportJSON(),
403
- isUnlinked: this.__isUnlinked
404
- };
405
- }
406
683
 
407
684
  // insertNewAfter is deliberately not overridden: LinkNode's implementation
408
685
  // uses $copyNode, which runs clone() + afterCloneFrom() and so carries the
@@ -40,7 +40,10 @@ declare export class LinkNode extends ElementNode {
40
40
  __title: null | string;
41
41
  constructor(url?: string, attributes?: LinkAttributes, key?: NodeKey): void;
42
42
  createDOM(config: EditorConfig): HTMLElement;
43
- exportJSON(): SerializedLinkNode;
43
+ // See LexicalNode.exportJSON: one signature, the legacy form, and `false`
44
+ // rather than `boolean` so a compact call is refused instead of answered
45
+ // with a shape the value does not have.
46
+ exportJSON(compact?: false): SerializedLinkNode;
44
47
  getURL(): string;
45
48
  setURL(url: string): void;
46
49
  getTarget(): null | string;
@@ -6,4 +6,4 @@
6
6
  *
7
7
  */
8
8
 
9
- import{AriaLiveRegionExtension as t}from"@lexical/a11y";import{effect as e}from"@lexical/extension/signals";import{namedSignals as n}from"@lexical/extension/namedSignals";import{ElementNode as r,$getDocument as i,addClassNamesToElement as s,isHTMLAnchorElement as l,$copyNode as o,$isRangeSelection as u,$applyNodeReplacement as a,createCommand as c,$getSelection as g,$isNodeSelection as f,$findMatchingParent as d,$normalizeSelection__EXPERIMENTAL as h,$setSelection as p,$getChildCaret as m,$isElementNode as x,$insertNodeToNearestRootAtCaret as _,$rewindSiblingCaret as k,$caretFromPoint as b,$normalizeCaret as v,$isSiblingCaret as U,$setPointFromCaret as T,shallowMergeConfig as L,mergeRegister as C,COMMAND_PRIORITY_EDITOR as S,PASTE_COMMAND as y,$isTextNode as R,COMMAND_PRIORITY_LOW as A,registerEventListeners as P,isDOMNode as O,getNearestEditorFromDOMNode as N,$getNearestNodeFromDOMNode as w,TextNode as I,$isLineBreakNode as D,$createTextNode as M}from"lexical";import{sel as E,$distributeInlineWrapper as F,CoreImportExtension as W,DOMImportExtension as $}from"@lexical/html";import{objectKlassEquals as z}from"@lexical/utils";const K=new Set(["http:","https:","mailto:","sms:","tel:"]);class B extends r{__url;__target;__rel;__title;$config(){return this.config("link",{extends:r,importDOM:{a:()=>({conversion:H,priority:1})}})}constructor(t="",e={},n){super(n);const{target:r=null,rel:i=null,title:s=null}=e;this.__url=t,this.__target=r,this.__rel=i,this.__title=s}afterCloneFrom(t){super.afterCloneFrom(t),this.__url=t.__url,this.__rel=t.__rel,this.__target=t.__target,this.__title=t.__title}createDOM(t){const e=i().createElement("a");return this.updateLinkDOM(null,e,t),s(e,t.theme.link),e}updateLinkDOM(t,e,n){if(l(e)){t&&t.__url===this.__url||(e.href=this.sanitizeUrl(this.__url));for(const n of["target","rel","title"]){const r=`__${n}`,i=this[r];t&&t[r]===i||(i?e[n]=i:e.removeAttribute(n))}}}updateDOM(t,e,n){return this.updateLinkDOM(t,e,n),!1}updateFromJSON(t){return super.updateFromJSON(t).setURL(t.url).setRel(t.rel||null).setTarget(t.target||null).setTitle(t.title||null)}sanitizeUrl(t){const e=t;t=rt(t);try{const e=new URL(rt(t));if(!K.has(e.protocol))return"about:blank"}catch(t){const n=e.replace(/[\u0000-\u001F\u007F\s]/g,"").match(/^([a-z][a-z0-9+.-]*):/i);if(null!=n&&!K.has(`${n[1].toLowerCase()}:`))return"about:blank"}return t}exportJSON(){return{...super.exportJSON(),rel:this.getRel(),target:this.getTarget(),title:this.getTitle(),url:this.getURL()}}getURL(){return this.getLatest().__url}setURL(t){const e=this.getWritable();return e.__url=t,e}getTarget(){return this.getLatest().__target}setTarget(t){const e=this.getWritable();return e.__target=t,e}getRel(){return this.getLatest().__rel}setRel(t){const e=this.getWritable();return e.__rel=t,e}getTitle(){return this.getLatest().__title}setTitle(t){const e=this.getWritable();return e.__title=t,e}insertNewAfter(t,e=!0){const n=o(this);return this.insertAfter(n,e),n}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(t,e,n){if(!u(e))return!1;const r=e.anchor.getNode(),i=e.focus.getNode();return(this.is(r)||this.isParentOf(r))&&(this.is(i)||this.isParentOf(i))&&e.getTextContent().length>0}isEmailURI(){return this.getURL().startsWith("mailto:")}isWebSiteURI(){const t=this.getURL();return t.startsWith("https://")||t.startsWith("http://")}shouldMergeAdjacentLink(t){return this.getType()===t.getType()&&this.getURL()===t.getURL()&&this.getTarget()===t.getTarget()&&this.getRel()===t.getRel()&&this.getTitle()===t.getTitle()}}function J(t){const e=b(t,"next");return[e,e.getFlipped()]}function j(t,e){for(const n of e)if(n.origin.isAttached()){const e=v(n);return void T(t,e)}}function Z(t){const e=g();let n=null,r=null;function i(){u(e)&&(j(e.anchor,n),j(e.focus,r),h(e))}u(e)&&(n=J(e.anchor),r=J(e.focus));let s=!1;for(const e of m(t,"next")){const n=e.origin;if(x(n)&&!n.isInline()){const r=n.getChildren();if(r.length>0){const e=o(t);e.append(...r),n.append(e),s=!0}_(n,k(e),{$shouldSplit:()=>!1})}}function l(t,e,n){const[r,i]=t,s=t=>U(t)&&t.origin.is(e);if(!s(r)&&!s(i))return t;const l=v(m(n,"next"));return[l,l.getFlipped()]}if(t.isAttached()){const e=t.getPreviousSibling();if(q(e)&&e.shouldMergeAdjacentLink(t))return n&&(n=l(n,e,t)),r&&(r=l(r,e,t)),e.append(...t.getChildren()),t.remove(),void i();const o=t.getNextSibling();q(o)&&t.shouldMergeAdjacentLink(o)&&(n&&(n=l(n,t,o)),r&&(r=l(r,t,o)),t.append(...o.getChildren()),o.remove(),s=!0)}if(s){if(!t.canBeEmpty()&&t.isEmpty()){const e=t.getParent();t.remove(),e&&e.isEmpty()&&e.remove()}i()}}function H(t){let e=null;if(l(t)){const n=t.textContent;(null!==n&&""!==n||t.children.length>0)&&(e=G(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:e}}function G(t="",e){return a(new B(t,e))}function q(t){return t instanceof B}class Q extends B{__isUnlinked;constructor(t="",e={},n){super(t,e,n),this.__isUnlinked=void 0!==e.isUnlinked&&null!==e.isUnlinked&&e.isUnlinked}afterCloneFrom(t){super.afterCloneFrom(t),this.__isUnlinked=t.__isUnlinked}$config(){return this.config("autolink",{extends:B})}shouldMergeAdjacentLink(t){return!1}getIsUnlinked(){return this.getLatest().__isUnlinked}setIsUnlinked(t){const e=this.getWritable();return e.__isUnlinked=t,e}createDOM(t){return this.__isUnlinked?i().createElement("span"):super.createDOM(t)}updateDOM(t,e,n){return super.updateDOM(t,e,n)||t.__isUnlinked!==this.__isUnlinked}updateFromJSON(t){return super.updateFromJSON(t).setIsUnlinked(t.isUnlinked||!1)}exportJSON(){return{...super.exportJSON(),isUnlinked:this.__isUnlinked}}}function V(t="",e){return a(new Q(t,e))}function X(t){return t instanceof Q}const Y=/* @__PURE__ */c("TOGGLE_LINK_COMMAND");function tt(t,e){if("element"===t.type){const n=t.getNode();x(n)||function(t,...e){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",t);for(const t of e)r.append("v",t);throw n.search=r.toString(),Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}(252);return n.getChildren()[t.offset+e]||null}return null}function et(t,e={}){let n;if(t&&"object"==typeof t){const{url:r,...i}=t;n=r,e={...i,...e}}else n=t;const{target:r,title:i}=e,s=void 0===e.rel?"noreferrer":e.rel,l=g();if(null===l||!u(l)&&!f(l))return;if(f(l)){const t=l.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===n){const e=d(t,t=>!X(t)&&q(t));e&&(e.insertBefore(t),0===e.getChildren().length&&e.remove())}else{const e=d(t,t=>!X(t)&&q(t));if(e)e.setURL(n),void 0!==r&&e.setTarget(r),void 0!==s&&e.setRel(s),void 0!==i&&e.setTitle(i);else{const e=G(n,{rel:s,target:r,title:i});t.insertBefore(e),e.append(t)}}})}if(l.isCollapsed()&&null===n)for(const t of l.getNodes()){const e=d(t,t=>!X(t)&&q(t));return void(null!==e&&(e.getParentOrThrow().splice(e.getIndexWithinParent(),0,e.getChildren()),e.remove()))}const a=l.extract();if(null===n){const t=new Set;return void a.forEach(e=>{const n=d(e,t=>!X(t)&&q(t));if(null!==n){const e=n.getKey();if(t.has(e))return;!function(t,e){const n=new Set(e.filter(e=>t.isParentOf(e)).map(t=>t.getKey())),r=t.getChildren(),i=r=>n.has(r.getKey())||x(r)&&e.some(e=>t.isParentOf(e)&&r.isParentOf(e)),s=r.filter(i);if(s.length===r.length)return r.forEach(e=>t.insertBefore(e)),void t.remove();const l=r.findIndex(i),u=r.findLastIndex(i),a=0===l,c=u===r.length-1;if(a)s.forEach(e=>t.insertBefore(e));else if(c)for(let e=s.length-1;e>=0;e--)t.insertAfter(s[e]);else{for(let e=s.length-1;e>=0;e--)t.insertAfter(s[e]);const e=r.slice(u+1);if(e.length>0){const n=o(t);s[s.length-1].insertAfter(n),e.forEach(t=>n.append(t))}}}(n,a),t.add(e)}})}const c=new Set,m=t=>{c.has(t.getKey())||(c.add(t.getKey()),t.setURL(n),void 0!==r&&t.setTarget(r),void 0!==s&&t.setRel(s),void 0!==i&&t.setTitle(i))};if(1===a.length){const t=a[0],e=d(t,q);if(null!==e)return m(e)}!function(t){const e=g();if(!u(e))return t();const n=h(e),r=n.isBackward(),i=tt(n.anchor,r?-1:0),s=tt(n.focus,r?0:-1),l=t();if(i||s){const t=g();if(u(t)){const e=t.clone();if(i){const t=i.getParent();t&&e.anchor.set(t.getKey(),i.getIndexWithinParent()+(r?1:0),"element")}if(s){const t=s.getParent();t&&e.focus.set(t.getKey(),s.getIndexWithinParent()+(r?0:1),"element")}p(h(e))}}}(()=>{let t=null;for(const e of a){if(!e.isAttached())continue;const l=d(e,q);if(l){m(l);continue}if(x(e)){if(!e.isInline())continue;if(q(e)){if(!(X(e)||null!==t&&t.getParentOrThrow().isParentOf(e))){m(e),t=e;continue}for(const t of e.getChildren())e.insertBefore(t);e.remove();continue}}const o=e.getPreviousSibling();q(o)&&o.is(t)?o.append(e):(t=G(n,{rel:s,target:r,title:i}),e.insertAfter(t),t.append(e))}})}const nt=/^\+?[0-9\s()-]{5,}$/;function rt(t){return t.match(/^[a-z][a-z0-9+.-]*:/i)||t.match(/^[/#.]/)?t:t.includes("@")?`mailto:${t}`:nt.test(t)?`tel:${t}`:`https://${t}`}const it={build:(t,e)=>n(e),config:{created:"Link",createdMany:"%s links",destroyed:"Link removed",destroyedMany:"%s links removed",disabled:!1},dependencies:[t],name:"@lexical/link/AutoLinkAnnounce",register(n,r,i){const{created:s,createdMany:l,destroyed:o,destroyedMany:u,disabled:a}=i.getOutput(),{announce:c}=i.getDependency(t).output;return e(()=>a.value?void 0:n.registerMutationListener(Q,t=>{let e=0,n=0;for(const[,r]of t)"created"===r?e++:"destroyed"===r&&n++;e>0&&0===n?c(1===e?s.peek():l.peek().replace("%s",String(e))):n>0&&0===e&&c(1===n?o.peek():u.peek().replace("%s",String(n)))},{skipInitialization:!0}))}},st=[{$import:(t,e)=>{if(!e.textContent&&0===e.children.length)return[];const n=e.getAttribute("href")||"",r={rel:e.getAttribute("rel"),target:e.getAttribute("target"),title:e.getAttribute("title")};return F(t.$importChildren(e),()=>G(n,r))},match:/* @__PURE__ */E.tag("a"),name:"@lexical/link/a"}];function lt(t,n){return C(t.registerNodeTransform(B,Z),t.registerCommand(Y,t=>{const e=n.validateUrl.peek(),r=n.attributes.peek();if(null===t)return et(null),!0;if("string"==typeof t)return!(void 0!==e&&!e(t))&&(et(t,r),!0);{const{url:n,...i}=t;return!(void 0!==e&&!e(n))&&(et(n,{...r,...i}),!0)}},S),e(()=>{const e=n.validateUrl.value;if(!e)return;const r=n.attributes.value;return t.registerCommand(y,n=>{const i=g();if(!u(i)||i.isCollapsed()||!z(n,ClipboardEvent))return!1;if(null===n.clipboardData)return!1;const s=n.clipboardData.getData("text");if(!e(s))return!1;return!i.getNodes().some(t=>x(t)||R(t)&&!t.isSimpleText())&&(t.dispatchCommand(Y,{...r,url:s}),n.preventDefault(),!0)},A)}))}const ot={build:(t,e,r)=>n(e),config:{attributes:void 0,validateUrl:void 0},dependencies:[W,[$,{rules:st}]],mergeConfig(t,e){const n=L(t,e);return t.attributes&&(n.attributes=L(t.attributes,n.attributes)),n},name:"@lexical/link/Link",nodes:()=>[B],register:(t,e,n)=>lt(t,n.getOutput())},ut={dependencies:[ot],name:"@lexical/link/Import"};function at(t,e,n={}){const r=n=>{const r=1===n.button;if("auxclick"===n.type&&!r)return;const i=n.target;if(!O(i))return;const s=N(i);if(null===s)return;let o=null,a=null,c=!1;if(s.update(()=>{const t=w(i);if(null!==t){const n=d(t,x);if(!e.disabled.peek())if(q(n))c=X(n)&&n.getIsUnlinked(),o=n.sanitizeUrl(n.getURL()),a=n.getTarget();else{const t=function(t,e){let n=t;for(;null!=n;){if(e(n))return n;n=n.parentNode}return null}(i,l);null!==t&&(o=t.href,a=t.target)}}}),null===o||""===o||c)return;const f=t.read("latest",g);!u(f)||f.isCollapsed()?(window.open(o,e.newTab.peek()||r||n.metaKey||n.ctrlKey||"_blank"===a?"_blank":"_self"),n.preventDefault()):n.preventDefault()};return t.registerRootListener(t=>{if(t)return P(t,{auxclick:r,click:r},n)})}const ct={build:(t,e,r)=>n(e),config:{disabled:!1,newTab:!1},dependencies:[ot],name:"@lexical/link/ClickableLink",register:(t,e,n)=>at(t,n.getOutput())};function gt(t,e=t=>t){return n=>{const r=t.exec(n);return null===r?null:{index:r.index,length:r[0].length,text:r[0],url:e(r[0])}}}const ft=/((https?:\/\/(www\.)?)|(www\.))[-\p{L}\p{N}@:%._+~#=]{1,256}\.[\p{L}\p{N}]{1,6}(?:[-\p{L}\p{N}()@:%_+.~#?&//=]*[\p{L}\p{N}()@_~#?&//=])?/u,dt=t=>{const e=ft.exec(t);if(null===e)return null;let n=e[0],r=0;for(const t of n)"("===t?r++:")"===t&&r--;for(;r<0&&n.endsWith(")");)n=n.slice(0,-1),r++;return{index:e.index,length:n.length,text:n,url:n.startsWith("http")?n:`https://${n}`}},ht=/* @__PURE__ */gt(/(([^<>()[\]\\.,;:\s@"]{1,64}(\.[^<>()[\]\\.,;:\s@"]{1,64}){0,63})|(".{1,255}"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]{1,63}\.){1,127}[a-zA-Z]{2,63}))/,t=>`mailto:${t}`);function pt(t,e){for(let n=0;n<e.length;n++){const r=e[n](t);if(r)return r}return null}const mt=/[.,;\s]/;function xt(t,e){return e.test(t)}function _t(t,e){return xt(t[t.length-1],e)}function kt(t,e){return xt(t[0],e)}function bt(t,e){let n=t.getPreviousSibling();return x(n)&&(n=n.getLastDescendant()),null===n||D(n)||R(n)&&_t(n.getTextContent(),e)}function vt(t,e){let n=t.getNextSibling();return x(n)&&(n=n.getFirstDescendant()),null===n||D(n)||R(n)&&kt(n.getTextContent(),e)}function Ut(t,e,n,r,i){if(!(t>0?xt(r[t-1],n):bt(i[0],n)))return!1;return e<r.length?xt(r[e],n):vt(i[i.length-1],n)}function Tt(t,e,n){const r=[],i=[],s=[];let l=0,o=0;const u=[...t];for(;u.length>0;){const t=u[0],a=t.getTextContent().length,c=o;o+a<=e?(r.push(t),l+=a):c>=n?s.push(t):i.push(t),o+=a,u.shift()}return[l,r,i,s]}function Lt(t,e,n,r){const i=V(r.url,r.attributes);if(1===t.length){let s,l=t[0];0===e?[s,l]=l.splitText(n):[,s,l]=l.splitText(e,n);const o=M(r.text);return o.setFormat(s.getFormat()),o.setDetail(s.getDetail()),o.setStyle(s.getStyle()),i.append(o),s.replace(i),l}if(t.length>1){const r=t[0];let s,l=r.getTextContent().length;0===e?s=r:[,s]=r.splitText(e);const o=[];let a;for(let e=1;e<t.length;e++){const r=t[e],i=r.getTextContent().length,s=l;if(s<n)if(l+i<=n)o.push(r);else{const[t,e]=r.splitText(n-s);o.push(t),a=e}l+=i}const c=g(),d=c?c.getNodes().find(R):void 0,h=M(s.getTextContent());return h.setFormat(s.getFormat()),h.setDetail(s.getDetail()),h.setStyle(s.getStyle()),i.append(h,...o),d&&d===s&&(u(c)?h.select(c.anchor.offset,c.focus.offset):f(c)&&h.select(0,h.getTextContent().length)),s.replace(i),a}}function Ct(t,e,n,r){const i=t.getChildren(),s=i.length;for(let e=0;e<s;e++){const r=i[e];if(!R(r)||!r.isSimpleText())return St(t),void n(null,t.getURL())}const l=t.getTextContent(),o=pt(l,e);if(null===o||o.text!==l)return St(t),void n(null,t.getURL());if(!bt(t,r)||!vt(t,r))return St(t),void n(null,t.getURL());const u=t.getURL();u!==o.url&&(t.setURL(o.url),n(o.url,u)),o.attributes&&(t.getRel()!==o.attributes.rel&&t.setRel(o.attributes.rel||null),t.getTarget()!==o.attributes.target&&t.setTarget(o.attributes.target||null),t.getTitle()!==o.attributes.title&&t.setTitle(o.attributes.title||null))}function St(t){const e=t.getChildren();for(let n=e.length-1;n>=0;n--)t.insertAfter(e[n]);return t.remove(),e.map(t=>t.getLatest())}const yt={changeHandlers:[],excludeParents:[],matchers:[],separatorRegex:mt};function Rt(t,e=yt){const{matchers:n,changeHandlers:r,excludeParents:i,separatorRegex:s=mt}=e,l=(t,e)=>{for(const n of r)n(t,e)};return C(t.registerNodeTransform(I,t=>{const e=t.getParentOrThrow(),r=t.getPreviousSibling();if(X(e))Ct(e,n,l,s);else if(!q(e)&&!i.some(t=>t(e))){if(t.isSimpleText()&&(kt(t.getTextContent(),s)||!X(r))){const e=function(t){const e=[t];let n=t.getNextSibling();for(;null!==n&&R(n)&&n.isSimpleText()&&(e.push(n),!/[\s]/.test(n.getTextContent()));)n=n.getNextSibling();return e}(t);!function(t,e,n,r){for(const e of t){const t=e.getParent();if(X(t)&&!t.getIsUnlinked())return}let i=[...t];const s=i.map(t=>t.getTextContent()).join("");let l,o=s,u=0;for(;(l=pt(o,e))&&null!==l;){const t=l.index,e=t+l.length;if(Ut(u+t,u+e,r,s,i)){const[r,,s,a]=Tt(i,u+t,u+e);let c=!1;for(const t of s){const e=t.getParent();if(X(e)&&!e.getIsUnlinked()){c=!0;break}}if(c){u+=e,o=o.substring(e);continue}const g=Lt(s,u+t-r,u+e-r,l);i=g?[g,...a]:a,n(l.url,null),u=0}else u+=e;o=o.substring(e)}}(e,n,l,s)}!function(t,e,n,r){const i=t.getParent(),s=t.getPreviousSibling(),l=t.getNextSibling(),o=t.getTextContent();if(!X(i)||i.getIsUnlinked()){if(X(s)&&!s.getIsUnlinked()&&s.is(t.getPreviousSibling())&&t.getParent()===s.getParent()){if(!kt(o,r))return St(s),void n(null,s.getURL());if(u=o,s.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(u):/^\.[a-zA-Z0-9]{1,}/.test(u)){const i=s.getTextContent()+o,l=pt(i,e);null!==l&&l.text===i&&(s.append(t),Ct(s,e,n,r),n(null,s.getURL()))}}var u;!X(l)||l.getIsUnlinked()||_t(o,r)||l.is(t.getNextSibling())&&t.getParent()===l.getParent()&&(St(l),n(null,l.getURL()))}}(t,n,l,s)}}),t.registerCommand(Y,t=>{const e=g();if(null!==t||!u(e))return!1;return e.extract().forEach(t=>{const e=t.getParent();X(e)&&(e.setIsUnlinked(!e.getIsUnlinked()),e.markDirty())}),!1},A))}const At={config:yt,dependencies:[it,ot],mergeConfig(t,e){const n=L(t,e);for(const r of["matchers","changeHandlers","excludeParents"]){const i=e[r];Array.isArray(i)&&(n[r]=[...t[r],...i])}return n},name:"@lexical/link/AutoLink",nodes:[Q],register:Rt};export{V as $createAutoLinkNode,G as $createLinkNode,X as $isAutoLinkNode,q as $isLinkNode,et as $toggleLink,it as AutoLinkAnnounceExtension,At as AutoLinkExtension,Q as AutoLinkNode,ct as ClickableLinkExtension,ot as LinkExtension,ut as LinkImportExtension,st as LinkImportRules,B as LinkNode,Y as TOGGLE_LINK_COMMAND,ht as autoLinkEmailMatcher,dt as autoLinkUrlMatcher,gt as createLinkMatcherWithRegExp,rt as formatUrl,Rt as registerAutoLink,at as registerClickableLink,lt as registerLink};
9
+ import{AriaLiveRegionExtension as t}from"@lexical/a11y";import{effect as e}from"@lexical/extension/signals";import{namedSignals as n}from"@lexical/extension/namedSignals";import{getterTableOf as r,setterTableOf as i,setterDefaultOf as l,ElementNode as o,$getDocument as s,addClassNamesToElement as u,isHTMLAnchorElement as a,$copyNode as c,$isRangeSelection as f,$applyNodeReplacement as g,createCommand as d,$getSelection as _,$isNodeSelection as p,$findMatchingParent as h,nodeSchema as m,withField as x,stringValue as y,nullable as v,booleanValue as k,$normalizeSelection__EXPERIMENTAL as b,$setSelection as U,$getChildCaret as S,$isElementNode as T,$insertNodeToNearestRootAtCaret as L,$rewindSiblingCaret as C,$caretFromPoint as A,$normalizeCaret as N,$isSiblingCaret as R,$setPointFromCaret as P,shallowMergeConfig as O,mergeRegister as I,COMMAND_PRIORITY_EDITOR as w,PASTE_COMMAND as F,$isTextNode as D,COMMAND_PRIORITY_LOW as M,registerEventListeners as E,isDOMNode as z,getNearestEditorFromDOMNode as $,$getNearestNodeFromDOMNode as W,TextNode as K,$isLineBreakNode as B,$createTextNode as j}from"lexical";import{sel as J,$distributeInlineWrapper as Z,CoreImportExtension as H,DOMImportExtension as G}from"@lexical/html";import{objectKlassEquals as q}from"@lexical/utils";const Q=/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;function V(t,e){if("number"==typeof t)return Number.isFinite(t)?t:e;if("string"!=typeof t||!Q.test(t))return e;const n=Number(t);return Number.isFinite(n)?n:e}function X(t,e,n,r,i){const l=V(t,e);return l>=n&&l<=r&&Number.isInteger(l)?l:e}function Y(t,e){t.__rel=e.__rel,t.__target=e.__target,t.__title=e.__title,t.__url=e.__url}const tt=t=>{const e=r(t,"format"),n=i(t,"format"),o=l(t,"format");return{exportJSON:function(t){const n=t.__textFormat,r=t.__textStyle,i=(0!==n||""!==r)&&t.shouldSerializeTextStyles();return{children:[],rel:t.__rel,target:t.__target,title:t.__title,url:t.__url,direction:t.__dir,format:e[t.__format],indent:t.__indent,textFormat:0!==n&&i?n:void 0,textStyle:""!==r&&i?r:void 0,type:t.__type,version:1}},exportCompactJSON:function(t){const n=t.__textFormat,r=t.__textStyle,i=(0!==n||""!==r)&&t.shouldSerializeTextStyles(),l={type:t.__type,children:[]},o=t.__rel;null!=o&&(l.rel=o);const s=t.__target;null!=s&&(l.target=s);const u=t.__title;null!=u&&(l.title=u);const a=t.__url;void 0!==a&&""!==a&&(l.url=a);const c=t.__dir;null!=c&&(l.direction=c);const f=e[t.__format];void 0!==f&&""!==f&&(l.format=f);const g=t.__indent;return void 0!==g&&0!==g&&(l.indent=g),void 0!==n&&0!==n&&i&&(l.textFormat=n),void 0!==r&&""!==r&&i&&(l.textStyle=r),l},updateFromJSON:function(t,e){const r=e.direction;t.__dir=null===r||"ltr"===r||"rtl"===r?r:null;const i=e.format;t.__format="string"==typeof i&&i in n?n[i]:o,t.__indent=X(e.indent,0,0,Infinity),t.__textFormat=V(e.textFormat,0);const l=e.textStyle;t.__textStyle="string"==typeof l?l:"";const s=e.rel;t.__rel="string"==typeof s&&""!==s?s:null;const u=e.target;t.__target="string"==typeof u&&""!==u?u:null;const a=e.title;t.__title="string"==typeof a&&""!==a?a:null;const c=e.url;return t.__url="string"==typeof c?c:"",t},afterCloneFrom:Y}};function et(t,e){t.__isUnlinked=e.__isUnlinked}const nt=t=>{const e=r(t,"format"),n=i(t,"format"),o=l(t,"format");return{exportJSON:function(t){const n=t.__textFormat,r=t.__textStyle,i=(0!==n||""!==r)&&t.shouldSerializeTextStyles();return{children:[],isUnlinked:t.__isUnlinked,rel:t.__rel,target:t.__target,title:t.__title,url:t.__url,direction:t.__dir,format:e[t.__format],indent:t.__indent,textFormat:0!==n&&i?n:void 0,textStyle:""!==r&&i?r:void 0,type:t.__type,version:1}},exportCompactJSON:function(t){const n=t.__textFormat,r=t.__textStyle,i=(0!==n||""!==r)&&t.shouldSerializeTextStyles(),l={type:t.__type,children:[]},o=t.__isUnlinked;void 0!==o&&!1!==o&&(l.isUnlinked=o);const s=t.__rel;null!=s&&(l.rel=s);const u=t.__target;null!=u&&(l.target=u);const a=t.__title;null!=a&&(l.title=a);const c=t.__url;void 0!==c&&""!==c&&(l.url=c);const f=t.__dir;null!=f&&(l.direction=f);const g=e[t.__format];void 0!==g&&""!==g&&(l.format=g);const d=t.__indent;return void 0!==d&&0!==d&&(l.indent=d),void 0!==n&&0!==n&&i&&(l.textFormat=n),void 0!==r&&""!==r&&i&&(l.textStyle=r),l},updateFromJSON:function(t,e){const r=e.direction;t.__dir=null===r||"ltr"===r||"rtl"===r?r:null;const i=e.format;t.__format="string"==typeof i&&i in n?n[i]:o,t.__indent=X(e.indent,0,0,Infinity),t.__textFormat=V(e.textFormat,0);const l=e.textStyle;t.__textStyle="string"==typeof l?l:"";const s=e.rel;t.__rel="string"==typeof s&&""!==s?s:null;const u=e.target;t.__target="string"==typeof u&&""!==u?u:null;const a=e.title;t.__title="string"==typeof a&&""!==a?a:null;const c=e.url;t.__url="string"==typeof c?c:"";const f=e.isUnlinked;return t.__isUnlinked="boolean"==typeof f&&f,t},afterCloneFrom:et}},rt=new Set(["http:","https:","mailto:","sms:","tel:"]),it=/* @__PURE__ */m()({rel:/* @__PURE__ */x(/* @__PURE__ */v(/* @__PURE__ */y(),{defaultAsNull:!0}),{field:"__rel"}),target:/* @__PURE__ */x(/* @__PURE__ */v(/* @__PURE__ */y(),{defaultAsNull:!0}),{field:"__target"}),title:/* @__PURE__ */x(/* @__PURE__ */v(/* @__PURE__ */y(),{defaultAsNull:!0}),{field:"__title"}),url:/* @__PURE__ */x(/* @__PURE__ */y(),{field:"__url",getter:"getURL",setter:"setURL"})});class lt extends o{__url;__target;__rel;__title;$config(){return this.config("link",{extends:o,generated:tt,importDOM:{a:()=>({conversion:at,priority:1})},json:it})}constructor(t="",e={},n){super(n);const{target:r=null,rel:i=null,title:l=null}=e;this.__url=t,this.__target=r,this.__rel=i,this.__title=l}createDOM(t){const e=s().createElement("a");return this.updateLinkDOM(null,e,t),u(e,t.theme.link),e}updateLinkDOM(t,e,n){if(a(e)){t&&t.__url===this.__url||(e.href=this.sanitizeUrl(this.__url));for(const n of["target","rel","title"]){const r=`__${n}`,i=this[r];t&&t[r]===i||(i?e[n]=i:e.removeAttribute(n))}}}updateDOM(t,e,n){return this.updateLinkDOM(t,e,n),!1}sanitizeUrl(t){const e=t;t=vt(t);try{const e=new URL(vt(t));if(!rt.has(e.protocol))return"about:blank"}catch(t){const n=e.replace(/[\u0000-\u001F\u007F\s]/g,"").match(/^([a-z][a-z0-9+.-]*):/i);if(null!=n&&!rt.has(`${n[1].toLowerCase()}:`))return"about:blank"}return t}getURL(){return this.getLatest().__url}setURL(t){const e=this.getWritable();return e.__url=t,e}getTarget(){return this.getLatest().__target}setTarget(t){const e=this.getWritable();return e.__target=t,e}getRel(){return this.getLatest().__rel}setRel(t){const e=this.getWritable();return e.__rel=t,e}getTitle(){return this.getLatest().__title}setTitle(t){const e=this.getWritable();return e.__title=t,e}insertNewAfter(t,e=!0){const n=c(this);return this.insertAfter(n,e),n}canInsertTextBefore(){return!1}canInsertTextAfter(){return!1}canBeEmpty(){return!1}isInline(){return!0}extractWithChild(t,e,n){if(!f(e))return!1;const r=e.anchor.getNode(),i=e.focus.getNode();return(this.is(r)||this.isParentOf(r))&&(this.is(i)||this.isParentOf(i))&&e.getTextContent().length>0}isEmailURI(){return this.getURL().startsWith("mailto:")}isWebSiteURI(){const t=this.getURL();return t.startsWith("https://")||t.startsWith("http://")}shouldMergeAdjacentLink(t){return this.getType()===t.getType()&&this.getURL()===t.getURL()&&this.getTarget()===t.getTarget()&&this.getRel()===t.getRel()&&this.getTitle()===t.getTitle()}}function ot(t){const e=A(t,"next");return[e,e.getFlipped()]}function st(t,e){for(const n of e)if(n.origin.isAttached()){const e=N(n);return void P(t,e)}}function ut(t){const e=_();let n=null,r=null;function i(){f(e)&&(st(e.anchor,n),st(e.focus,r),b(e))}f(e)&&(n=ot(e.anchor),r=ot(e.focus));let l=!1;for(const e of S(t,"next")){const n=e.origin;if(T(n)&&!n.isInline()){const r=n.getChildren();if(r.length>0){const e=c(t);e.append(...r),n.append(e),l=!0}L(n,C(e),{$shouldSplit:()=>!1})}}function o(t,e,n){const[r,i]=t,l=t=>R(t)&&t.origin.is(e);if(!l(r)&&!l(i))return t;const o=N(S(n,"next"));return[o,o.getFlipped()]}if(t.isAttached()){const e=t.getPreviousSibling();if(ft(e)&&e.shouldMergeAdjacentLink(t))return n&&(n=o(n,e,t)),r&&(r=o(r,e,t)),e.append(...t.getChildren()),t.remove(),void i();const s=t.getNextSibling();ft(s)&&t.shouldMergeAdjacentLink(s)&&(n&&(n=o(n,t,s)),r&&(r=o(r,t,s)),t.append(...s.getChildren()),s.remove(),l=!0)}if(l){if(!t.canBeEmpty()&&t.isEmpty()){const e=t.getParent();t.remove(),e&&e.isEmpty()&&e.remove()}i()}}function at(t){let e=null;if(a(t)){const n=t.textContent;(null!==n&&""!==n||t.children.length>0)&&(e=ct(t.getAttribute("href")||"",{rel:t.getAttribute("rel"),target:t.getAttribute("target"),title:t.getAttribute("title")}))}return{node:e}}function ct(t="",e){return g(new lt(t,e))}function ft(t){return t instanceof lt}const gt=/* @__PURE__ */m()({isUnlinked:/* @__PURE__ */x(/* @__PURE__ */k(),{field:"__isUnlinked"})});class dt extends lt{__isUnlinked;constructor(t="",e={},n){super(t,e,n),this.__isUnlinked=void 0!==e.isUnlinked&&null!==e.isUnlinked&&e.isUnlinked}$config(){return this.config("autolink",{extends:lt,generated:nt,json:gt})}shouldMergeAdjacentLink(t){return!1}getIsUnlinked(){return this.getLatest().__isUnlinked}setIsUnlinked(t){const e=this.getWritable();return e.__isUnlinked=t,e}createDOM(t){return this.__isUnlinked?s().createElement("span"):super.createDOM(t)}updateDOM(t,e,n){return super.updateDOM(t,e,n)||t.__isUnlinked!==this.__isUnlinked}}function _t(t="",e){return g(new dt(t,e))}function pt(t){return t instanceof dt}const ht=/* @__PURE__ */d("TOGGLE_LINK_COMMAND");function mt(t,e){if("element"===t.type){const n=t.getNode();T(n)||function(t,...e){const n=new URL("https://lexical.dev/docs/error"),r=new URLSearchParams;r.append("code",t);for(const t of e)r.append("v",t);throw n.search=r.toString(),Error(`Minified Lexical error #${t}; visit ${n.toString()} for the full message or use the non-minified dev environment for full errors and additional helpful warnings.`)}(252);return n.getChildren()[t.offset+e]||null}return null}function xt(t,e={}){let n;if(t&&"object"==typeof t){const{url:r,...i}=t;n=r,e={...i,...e}}else n=t;const{target:r,title:i}=e,l=void 0===e.rel?"noreferrer":e.rel,o=_();if(null===o||!f(o)&&!p(o))return;if(p(o)){const t=o.getNodes();if(0===t.length)return;return void t.forEach(t=>{if(null===n){const e=h(t,t=>!pt(t)&&ft(t));e&&(e.insertBefore(t),0===e.getChildren().length&&e.remove())}else{const e=h(t,t=>!pt(t)&&ft(t));if(e)e.setURL(n),void 0!==r&&e.setTarget(r),void 0!==l&&e.setRel(l),void 0!==i&&e.setTitle(i);else{const e=ct(n,{rel:l,target:r,title:i});t.insertBefore(e),e.append(t)}}})}if(o.isCollapsed()&&null===n)for(const t of o.getNodes()){const e=h(t,t=>!pt(t)&&ft(t));return void(null!==e&&(e.getParentOrThrow().splice(e.getIndexWithinParent(),0,e.getChildren()),e.remove()))}const s=o.extract();if(null===n){const t=new Set;return void s.forEach(e=>{const n=h(e,t=>!pt(t)&&ft(t));if(null!==n){const e=n.getKey();if(t.has(e))return;!function(t,e){const n=new Set(e.filter(e=>t.isParentOf(e)).map(t=>t.getKey())),r=t.getChildren(),i=r=>n.has(r.getKey())||T(r)&&e.some(e=>t.isParentOf(e)&&r.isParentOf(e)),l=r.filter(i);if(l.length===r.length)return r.forEach(e=>t.insertBefore(e)),void t.remove();const o=r.findIndex(i),s=r.findLastIndex(i),u=0===o,a=s===r.length-1;if(u)l.forEach(e=>t.insertBefore(e));else if(a)for(let e=l.length-1;e>=0;e--)t.insertAfter(l[e]);else{for(let e=l.length-1;e>=0;e--)t.insertAfter(l[e]);const e=r.slice(s+1);if(e.length>0){const n=c(t);l[l.length-1].insertAfter(n),e.forEach(t=>n.append(t))}}}(n,s),t.add(e)}})}const u=new Set,a=t=>{u.has(t.getKey())||(u.add(t.getKey()),t.setURL(n),void 0!==r&&t.setTarget(r),void 0!==l&&t.setRel(l),void 0!==i&&t.setTitle(i))};if(1===s.length){const t=s[0],e=h(t,ft);if(null!==e)return a(e)}!function(t){const e=_();if(!f(e))return t();const n=b(e),r=n.isBackward(),i=mt(n.anchor,r?-1:0),l=mt(n.focus,r?0:-1),o=t();if(i||l){const t=_();if(f(t)){const e=t.clone();if(i){const t=i.getParent();t&&e.anchor.set(t.getKey(),i.getIndexWithinParent()+(r?1:0),"element")}if(l){const t=l.getParent();t&&e.focus.set(t.getKey(),l.getIndexWithinParent()+(r?0:1),"element")}U(b(e))}}}(()=>{let t=null;for(const e of s){if(!e.isAttached())continue;const o=h(e,ft);if(o){a(o);continue}if(T(e)){if(!e.isInline())continue;if(ft(e)){if(!(pt(e)||null!==t&&t.getParentOrThrow().isParentOf(e))){a(e),t=e;continue}for(const t of e.getChildren())e.insertBefore(t);e.remove();continue}}const s=e.getPreviousSibling();ft(s)&&s.is(t)?s.append(e):(t=ct(n,{rel:l,target:r,title:i}),e.insertAfter(t),t.append(e))}})}const yt=/^\+?[0-9\s()-]{5,}$/;function vt(t){return t.match(/^[a-z][a-z0-9+.-]*:/i)||t.match(/^[/#.]/)?t:t.includes("@")?`mailto:${t}`:yt.test(t)?`tel:${t}`:`https://${t}`}const kt={build:(t,e)=>n(e),config:{created:"Link",createdMany:"%s links",destroyed:"Link removed",destroyedMany:"%s links removed",disabled:!1},dependencies:[t],name:"@lexical/link/AutoLinkAnnounce",register(n,r,i){const{created:l,createdMany:o,destroyed:s,destroyedMany:u,disabled:a}=i.getOutput(),{announce:c}=i.getDependency(t).output;return e(()=>a.value?void 0:n.registerMutationListener(dt,t=>{let e=0,n=0;for(const[,r]of t)"created"===r?e++:"destroyed"===r&&n++;e>0&&0===n?c(1===e?l.peek():o.peek().replace("%s",String(e))):n>0&&0===e&&c(1===n?s.peek():u.peek().replace("%s",String(n)))},{skipInitialization:!0}))}},bt=[{$import:(t,e)=>{if(!e.textContent&&0===e.children.length)return[];const n=e.getAttribute("href")||"",r={rel:e.getAttribute("rel"),target:e.getAttribute("target"),title:e.getAttribute("title")};return Z(t.$importChildren(e),()=>ct(n,r))},match:/* @__PURE__ */J.tag("a"),name:"@lexical/link/a"}];function Ut(t,n){return I(t.registerNodeTransform(lt,ut),t.registerCommand(ht,t=>{const e=n.validateUrl.peek(),r=n.attributes.peek();if(null===t)return xt(null),!0;if("string"==typeof t)return!(void 0!==e&&!e(t))&&(xt(t,r),!0);{const{url:n,...i}=t;return!(void 0!==e&&!e(n))&&(xt(n,{...r,...i}),!0)}},w),e(()=>{const e=n.validateUrl.value;if(!e)return;const r=n.attributes.value;return t.registerCommand(F,n=>{const i=_();if(!f(i)||i.isCollapsed()||!q(n,ClipboardEvent))return!1;if(null===n.clipboardData)return!1;const l=n.clipboardData.getData("text");if(!e(l))return!1;return!i.getNodes().some(t=>T(t)||D(t)&&!t.isSimpleText())&&(t.dispatchCommand(ht,{...r,url:l}),n.preventDefault(),!0)},M)}))}const St={build:(t,e,r)=>n(e),config:{attributes:void 0,validateUrl:void 0},dependencies:[H,[G,{rules:bt}]],mergeConfig(t,e){const n=O(t,e);return t.attributes&&(n.attributes=O(t.attributes,n.attributes)),n},name:"@lexical/link/Link",nodes:()=>[lt],register:(t,e,n)=>Ut(t,n.getOutput())},Tt={dependencies:[St],name:"@lexical/link/Import"};function Lt(t,e,n={}){const r=n=>{const r=1===n.button;if("auxclick"===n.type&&!r)return;const i=n.target;if(!z(i))return;const l=$(i);if(null===l)return;let o=null,s=null,u=!1;if(l.update(()=>{const t=W(i);if(null!==t){const n=h(t,T);if(!e.disabled.peek())if(ft(n))u=pt(n)&&n.getIsUnlinked(),o=n.sanitizeUrl(n.getURL()),s=n.getTarget();else{const t=function(t,e){let n=t;for(;null!=n;){if(e(n))return n;n=n.parentNode}return null}(i,a);null!==t&&(o=t.href,s=t.target)}}}),null===o||""===o||u)return;const c=t.read("latest",_);!f(c)||c.isCollapsed()?(window.open(o,e.newTab.peek()||r||n.metaKey||n.ctrlKey||"_blank"===s?"_blank":"_self"),n.preventDefault()):n.preventDefault()};return t.registerRootListener(t=>{if(t)return E(t,{auxclick:r,click:r},n)})}const Ct={build:(t,e,r)=>n(e),config:{disabled:!1,newTab:!1},dependencies:[St],name:"@lexical/link/ClickableLink",register:(t,e,n)=>Lt(t,n.getOutput())};function At(t,e=t=>t){return n=>{const r=t.exec(n);return null===r?null:{index:r.index,length:r[0].length,text:r[0],url:e(r[0])}}}const Nt=/((https?:\/\/(www\.)?)|(www\.))[-\p{L}\p{N}@:%._+~#=]{1,256}\.[\p{L}\p{N}]{1,6}(?:[-\p{L}\p{N}()@:%_+.~#?&//=]*[\p{L}\p{N}()@_~#?&//=])?/u,Rt=t=>{const e=Nt.exec(t);if(null===e)return null;let n=e[0],r=0;for(const t of n)"("===t?r++:")"===t&&r--;for(;r<0&&n.endsWith(")");)n=n.slice(0,-1),r++;return{index:e.index,length:n.length,text:n,url:n.startsWith("http")?n:`https://${n}`}},Pt=/* @__PURE__ */At(/(([^<>()[\]\\.,;:\s@"]{1,64}(\.[^<>()[\]\\.,;:\s@"]{1,64}){0,63})|(".{1,255}"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]{1,63}\.){1,127}[a-zA-Z]{2,63}))/,t=>`mailto:${t}`);function Ot(t,e){for(let n=0;n<e.length;n++){const r=e[n](t);if(r)return r}return null}const It=/[.,;\s]/;function wt(t,e){return e.test(t)}function Ft(t,e){return wt(t[t.length-1],e)}function Dt(t,e){return wt(t[0],e)}function Mt(t,e){let n=t.getPreviousSibling();return T(n)&&(n=n.getLastDescendant()),null===n||B(n)||D(n)&&Ft(n.getTextContent(),e)}function Et(t,e){let n=t.getNextSibling();return T(n)&&(n=n.getFirstDescendant()),null===n||B(n)||D(n)&&Dt(n.getTextContent(),e)}function zt(t,e,n,r,i){if(!(t>0?wt(r[t-1],n):Mt(i[0],n)))return!1;return e<r.length?wt(r[e],n):Et(i[i.length-1],n)}function $t(t,e,n){const r=[],i=[],l=[];let o=0,s=0;const u=[...t];for(;u.length>0;){const t=u[0],a=t.getTextContent().length,c=s;s+a<=e?(r.push(t),o+=a):c>=n?l.push(t):i.push(t),s+=a,u.shift()}return[o,r,i,l]}function Wt(t,e,n,r){const i=_t(r.url,r.attributes);if(1===t.length){let l,o=t[0];0===e?[l,o]=o.splitText(n):[,l,o]=o.splitText(e,n);const s=j(r.text);return s.setFormat(l.getFormat()),s.setDetail(l.getDetail()),s.setStyle(l.getStyle()),i.append(s),l.replace(i),o}if(t.length>1){const r=t[0];let l,o=r.getTextContent().length;0===e?l=r:[,l]=r.splitText(e);const s=[];let u;for(let e=1;e<t.length;e++){const r=t[e],i=r.getTextContent().length,l=o;if(l<n)if(o+i<=n)s.push(r);else{const[t,e]=r.splitText(n-l);s.push(t),u=e}o+=i}const a=_(),c=a?a.getNodes().find(D):void 0,g=j(l.getTextContent());return g.setFormat(l.getFormat()),g.setDetail(l.getDetail()),g.setStyle(l.getStyle()),i.append(g,...s),c&&c===l&&(f(a)?g.select(a.anchor.offset,a.focus.offset):p(a)&&g.select(0,g.getTextContent().length)),l.replace(i),u}}function Kt(t,e,n,r){const i=t.getChildren(),l=i.length;for(let e=0;e<l;e++){const r=i[e];if(!D(r)||!r.isSimpleText())return Bt(t),void n(null,t.getURL())}const o=t.getTextContent(),s=Ot(o,e);if(null===s||s.text!==o)return Bt(t),void n(null,t.getURL());if(!Mt(t,r)||!Et(t,r))return Bt(t),void n(null,t.getURL());const u=t.getURL();u!==s.url&&(t.setURL(s.url),n(s.url,u)),s.attributes&&(t.getRel()!==s.attributes.rel&&t.setRel(s.attributes.rel||null),t.getTarget()!==s.attributes.target&&t.setTarget(s.attributes.target||null),t.getTitle()!==s.attributes.title&&t.setTitle(s.attributes.title||null))}function Bt(t){const e=t.getChildren();for(let n=e.length-1;n>=0;n--)t.insertAfter(e[n]);return t.remove(),e.map(t=>t.getLatest())}const jt={changeHandlers:[],excludeParents:[],matchers:[],separatorRegex:It};function Jt(t,e=jt){const{matchers:n,changeHandlers:r,excludeParents:i,separatorRegex:l=It}=e,o=(t,e)=>{for(const n of r)n(t,e)};return I(t.registerNodeTransform(K,t=>{const e=t.getParentOrThrow(),r=t.getPreviousSibling();if(pt(e))Kt(e,n,o,l);else if(!ft(e)&&!i.some(t=>t(e))){if(t.isSimpleText()&&(Dt(t.getTextContent(),l)||!pt(r))){const e=function(t){const e=[t];let n=t.getNextSibling();for(;null!==n&&D(n)&&n.isSimpleText()&&(e.push(n),!/[\s]/.test(n.getTextContent()));)n=n.getNextSibling();return e}(t);!function(t,e,n,r){for(const e of t){const t=e.getParent();if(pt(t)&&!t.getIsUnlinked())return}let i=[...t];const l=i.map(t=>t.getTextContent()).join("");let o,s=l,u=0;for(;(o=Ot(s,e))&&null!==o;){const t=o.index,e=t+o.length;if(zt(u+t,u+e,r,l,i)){const[r,,l,a]=$t(i,u+t,u+e);let c=!1;for(const t of l){const e=t.getParent();if(pt(e)&&!e.getIsUnlinked()){c=!0;break}}if(c){u+=e,s=s.substring(e);continue}const f=Wt(l,u+t-r,u+e-r,o);i=f?[f,...a]:a,n(o.url,null),u=0}else u+=e;s=s.substring(e)}}(e,n,o,l)}!function(t,e,n,r){const i=t.getParent(),l=t.getPreviousSibling(),o=t.getNextSibling(),s=t.getTextContent();if(!pt(i)||i.getIsUnlinked()){if(pt(l)&&!l.getIsUnlinked()&&l.is(t.getPreviousSibling())&&t.getParent()===l.getParent()){if(!Dt(s,r))return Bt(l),void n(null,l.getURL());if(u=s,l.isEmailURI()?/^\.[a-zA-Z]{2,}/.test(u):/^\.[a-zA-Z0-9]{1,}/.test(u)){const i=l.getTextContent()+s,o=Ot(i,e);null!==o&&o.text===i&&(l.append(t),Kt(l,e,n,r),n(null,l.getURL()))}}var u;!pt(o)||o.getIsUnlinked()||Ft(s,r)||o.is(t.getNextSibling())&&t.getParent()===o.getParent()&&(Bt(o),n(null,o.getURL()))}}(t,n,o,l)}}),t.registerCommand(ht,t=>{const e=_();if(null!==t||!f(e))return!1;return e.extract().forEach(t=>{const e=t.getParent();pt(e)&&(e.setIsUnlinked(!e.getIsUnlinked()),e.markDirty())}),!1},M))}const Zt={config:jt,dependencies:[kt,St],mergeConfig(t,e){const n=O(t,e);for(const r of["matchers","changeHandlers","excludeParents"]){const i=e[r];Array.isArray(i)&&(n[r]=[...t[r],...i])}return n},name:"@lexical/link/AutoLink",nodes:[dt],register:Jt};export{_t as $createAutoLinkNode,ct as $createLinkNode,pt as $isAutoLinkNode,ft as $isLinkNode,xt as $toggleLink,kt as AutoLinkAnnounceExtension,Zt as AutoLinkExtension,dt as AutoLinkNode,Ct as ClickableLinkExtension,St as LinkExtension,Tt as LinkImportExtension,bt as LinkImportRules,lt as LinkNode,ht as TOGGLE_LINK_COMMAND,Pt as autoLinkEmailMatcher,Rt as autoLinkUrlMatcher,At as createLinkMatcherWithRegExp,vt as formatUrl,Jt as registerAutoLink,Lt as registerClickableLink,Ut as registerLink};
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3
+ *
4
+ * This source code is licensed under the MIT license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ */
8
+ import type { AutoLinkNode, LinkNode } from './LexicalLinkNode.js';
9
+ import { type GeneratedJSONFactory } from 'lexical';
10
+ /**
11
+ * LinkNode's schema-declared fields, for a clone. Generated from that
12
+ * schema; do not edit by hand.
13
+ *
14
+ * @internal
15
+ */
16
+ export declare function afterCloneLinkNode(node: LinkNode, prevNode: LinkNode): void;
17
+ /** LinkNode's generated implementations, for its `$config`. @internal */
18
+ export declare const GENERATED_LINK: GeneratedJSONFactory;
19
+ /**
20
+ * AutoLinkNode's schema-declared fields, for a clone. Generated from that
21
+ * schema; do not edit by hand.
22
+ *
23
+ * @internal
24
+ */
25
+ export declare function afterCloneAutoLinkNode(node: AutoLinkNode, prevNode: AutoLinkNode): void;
26
+ /** AutoLinkNode's generated implementations, for its `$config`. @internal */
27
+ export declare const GENERATED_AUTOLINK: GeneratedJSONFactory;
@@ -5,7 +5,7 @@
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  *
7
7
  */
8
- import { type BaseSelection, type DOMConversionOutput, type EditorConfig, ElementNode, type LexicalCommand, type LexicalNode, type LexicalUpdateJSON, type NodeKey, type RangeSelection, type SerializedElementNode, type Spread } from 'lexical';
8
+ import { type BaseSelection, type DOMConversionOutput, type EditorConfig, ElementNode, type LexicalCommand, type LexicalNode, type LexicalParseJSON, type NodeKey, type RangeSelection, type SerializedElementNode, type SerializedPartial, type Spread } from 'lexical';
9
9
  export type LinkAttributes = {
10
10
  rel?: null | string;
11
11
  target?: null | string;
@@ -18,6 +18,11 @@ export type SerializedLinkNode = Spread<{
18
18
  url: string;
19
19
  }, Spread<LinkAttributes, SerializedElementNode>>;
20
20
  type LinkHTMLElementType = HTMLAnchorElement | HTMLSpanElement;
21
+ export interface LinkNode {
22
+ exportJSON(compact?: false): SerializedLinkNode;
23
+ exportJSON(compact: boolean): SerializedPartial<SerializedLinkNode>;
24
+ updateFromJSON(serializedNode: LexicalParseJSON<SerializedLinkNode>): this;
25
+ }
21
26
  /** @noInheritDoc */
22
27
  export declare class LinkNode extends ElementNode {
23
28
  /** @internal */
@@ -28,33 +33,55 @@ export declare class LinkNode extends ElementNode {
28
33
  __rel: null | string;
29
34
  /** @internal */
30
35
  __title: null | string;
31
- $config(): import("lexical").BaseStaticNodeConfig & {
36
+ $config(): import("lexical").BaseStaticNodeConfig & import("lexical").StaticNodeConfigAccessor<{
37
+ readonly $transform: (node: ElementNode) => void;
38
+ readonly extends: typeof LexicalNode;
39
+ readonly generated: import("lexical").GeneratedJSONFactory;
40
+ readonly json: import("lexical").NodeSerializationSchema<ElementNode, {
41
+ readonly direction?: "ltr" | "rtl" | null | undefined;
42
+ readonly format?: "" | "left" | "start" | "center" | "right" | "end" | "justify" | undefined;
43
+ readonly indent?: string | number | undefined;
44
+ readonly textFormat?: string | number | undefined;
45
+ readonly textStyle?: string | undefined;
46
+ }>;
47
+ }> & {
32
48
  readonly link?: {
33
49
  readonly extends: typeof ElementNode;
50
+ readonly generated: import("lexical").GeneratedJSONFactory;
34
51
  readonly importDOM: {
35
52
  readonly a: () => {
36
53
  conversion: typeof $convertAnchorElement;
37
54
  priority: 1;
38
55
  };
39
56
  };
57
+ readonly json: import("lexical").NodeSerializationSchema<LinkNode, {
58
+ readonly rel?: string | null | undefined;
59
+ readonly target?: string | null | undefined;
60
+ readonly title?: string | null | undefined;
61
+ readonly url?: string | undefined;
62
+ }>;
40
63
  } | undefined;
41
64
  } & import("lexical").StaticNodeTypeAccessor<"link"> & import("lexical").StaticNodeConfigAccessor<{
42
65
  readonly extends: typeof ElementNode;
66
+ readonly generated: import("lexical").GeneratedJSONFactory;
43
67
  readonly importDOM: {
44
68
  readonly a: () => {
45
69
  conversion: typeof $convertAnchorElement;
46
70
  priority: 1;
47
71
  };
48
72
  };
73
+ readonly json: import("lexical").NodeSerializationSchema<LinkNode, {
74
+ readonly rel?: string | null | undefined;
75
+ readonly target?: string | null | undefined;
76
+ readonly title?: string | null | undefined;
77
+ readonly url?: string | undefined;
78
+ }>;
49
79
  }>;
50
80
  constructor(url?: string, attributes?: LinkAttributes, key?: NodeKey);
51
- afterCloneFrom(prevNode: this): void;
52
81
  createDOM(config: EditorConfig): LinkHTMLElementType;
53
82
  updateLinkDOM(prevNode: this | null, anchor: LinkHTMLElementType, config: EditorConfig): void;
54
83
  updateDOM(prevNode: this, anchor: LinkHTMLElementType, config: EditorConfig): boolean;
55
- updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedLinkNode>): this;
56
84
  sanitizeUrl(url: string): string;
57
- exportJSON(): SerializedLinkNode | SerializedAutoLinkNode;
58
85
  getURL(): string;
59
86
  setURL(url: string): this;
60
87
  getTarget(): null | string;
@@ -96,44 +123,79 @@ export declare function $isLinkNode(node: LexicalNode | null | undefined): node
96
123
  export type SerializedAutoLinkNode = Spread<{
97
124
  isUnlinked: boolean;
98
125
  }, SerializedLinkNode>;
126
+ export interface AutoLinkNode {
127
+ exportJSON(compact?: false): SerializedAutoLinkNode;
128
+ exportJSON(compact: boolean): SerializedPartial<SerializedAutoLinkNode>;
129
+ updateFromJSON(serializedNode: LexicalParseJSON<SerializedAutoLinkNode>): this;
130
+ }
99
131
  export declare class AutoLinkNode extends LinkNode {
100
132
  /** @internal */
101
133
  /** Indicates whether the autolink was ever unlinked. **/
102
134
  __isUnlinked: boolean;
103
135
  constructor(url?: string, attributes?: AutoLinkAttributes, key?: NodeKey);
104
- afterCloneFrom(prevNode: this): void;
105
- $config(): import("lexical").BaseStaticNodeConfig & {
136
+ $config(): import("lexical").BaseStaticNodeConfig & import("lexical").StaticNodeConfigAccessor<{
137
+ readonly $transform: (node: ElementNode) => void;
138
+ readonly extends: typeof LexicalNode;
139
+ readonly generated: import("lexical").GeneratedJSONFactory;
140
+ readonly json: import("lexical").NodeSerializationSchema<ElementNode, {
141
+ readonly direction?: "ltr" | "rtl" | null | undefined;
142
+ readonly format?: "" | "left" | "start" | "center" | "right" | "end" | "justify" | undefined;
143
+ readonly indent?: string | number | undefined;
144
+ readonly textFormat?: string | number | undefined;
145
+ readonly textStyle?: string | undefined;
146
+ }>;
147
+ }> & {
106
148
  readonly link?: {
107
149
  readonly extends: typeof ElementNode;
150
+ readonly generated: import("lexical").GeneratedJSONFactory;
108
151
  readonly importDOM: {
109
152
  readonly a: () => {
110
153
  conversion: typeof $convertAnchorElement;
111
154
  priority: 1;
112
155
  };
113
156
  };
157
+ readonly json: import("lexical").NodeSerializationSchema<LinkNode, {
158
+ readonly rel?: string | null | undefined;
159
+ readonly target?: string | null | undefined;
160
+ readonly title?: string | null | undefined;
161
+ readonly url?: string | undefined;
162
+ }>;
114
163
  } | undefined;
115
164
  } & import("lexical").StaticNodeTypeAccessor<"link"> & import("lexical").StaticNodeConfigAccessor<{
116
165
  readonly extends: typeof ElementNode;
166
+ readonly generated: import("lexical").GeneratedJSONFactory;
117
167
  readonly importDOM: {
118
168
  readonly a: () => {
119
169
  conversion: typeof $convertAnchorElement;
120
170
  priority: 1;
121
171
  };
122
172
  };
173
+ readonly json: import("lexical").NodeSerializationSchema<LinkNode, {
174
+ readonly rel?: string | null | undefined;
175
+ readonly target?: string | null | undefined;
176
+ readonly title?: string | null | undefined;
177
+ readonly url?: string | undefined;
178
+ }>;
123
179
  }> & {
124
180
  readonly autolink?: {
125
181
  readonly extends: typeof LinkNode;
182
+ readonly generated: import("lexical").GeneratedJSONFactory;
183
+ readonly json: import("lexical").NodeSerializationSchema<AutoLinkNode, {
184
+ readonly isUnlinked?: boolean | undefined;
185
+ }>;
126
186
  } | undefined;
127
187
  } & import("lexical").StaticNodeTypeAccessor<"autolink"> & import("lexical").StaticNodeConfigAccessor<{
128
188
  readonly extends: typeof LinkNode;
189
+ readonly generated: import("lexical").GeneratedJSONFactory;
190
+ readonly json: import("lexical").NodeSerializationSchema<AutoLinkNode, {
191
+ readonly isUnlinked?: boolean | undefined;
192
+ }>;
129
193
  }>;
130
194
  shouldMergeAdjacentLink(_otherLink: LinkNode): boolean;
131
195
  getIsUnlinked(): boolean;
132
196
  setIsUnlinked(value: boolean): this;
133
197
  createDOM(config: EditorConfig): LinkHTMLElementType;
134
198
  updateDOM(prevNode: this, anchor: LinkHTMLElementType, config: EditorConfig): boolean;
135
- updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedAutoLinkNode>): this;
136
- exportJSON(): SerializedAutoLinkNode;
137
199
  }
138
200
  /**
139
201
  * Takes a URL and creates an AutoLinkNode. AutoLinkNodes are generally automatically generated
package/package.json CHANGED
@@ -8,16 +8,16 @@
8
8
  "link"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.50.1-nightly.20260916.0",
11
+ "version": "0.51.0",
12
12
  "main": "./dist/LexicalLink.js",
13
13
  "types": "./dist/typescript-too-old.d.ts",
14
14
  "dependencies": {
15
- "@lexical/a11y": "0.50.1-nightly.20260916.0",
16
- "@lexical/extension": "0.50.1-nightly.20260916.0",
17
- "@lexical/html": "0.50.1-nightly.20260916.0",
18
- "@lexical/internal": "0.50.1-nightly.20260916.0",
19
- "@lexical/utils": "0.50.1-nightly.20260916.0",
20
- "lexical": "0.50.1-nightly.20260916.0"
15
+ "@lexical/a11y": "0.51.0",
16
+ "@lexical/extension": "0.51.0",
17
+ "@lexical/html": "0.51.0",
18
+ "@lexical/internal": "0.51.0",
19
+ "@lexical/utils": "0.51.0",
20
+ "lexical": "0.51.0"
21
21
  },
22
22
  "repository": {
23
23
  "type": "git",
@@ -0,0 +1,370 @@
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
+ // @generated by scripts/generate-node-json.mjs from each class's `json`
10
+ // schema. Run `pnpm run generate-node-json` to regenerate; do not edit.
11
+
12
+ // Keys are emitted in the order the schema-driven walk writes them, so the two
13
+ // produce byte-identical JSON. That order is the schema's, not alphabetical.
14
+ /* eslint-disable sort-keys-fix/sort-keys-fix */
15
+
16
+ import type {AutoLinkNode, LinkNode} from './LexicalLinkNode';
17
+
18
+ import {
19
+ type GeneratedJSONFactory,
20
+ getterTableOf,
21
+ setterDefaultOf,
22
+ setterTableOf,
23
+ } from 'lexical';
24
+
25
+ // The JSON number grammar, anchored, matching numberValue: `Number()` alone
26
+ // reads '0x10' as 16 and '' as 0, and neither is a shape a JSON encoder
27
+ // produces. Emitted from the same source the codegen verified against, so the
28
+ // two cannot be different functions.
29
+ const JSON_NUMBER = /^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/;
30
+
31
+ function num(v: unknown, d: number): number {
32
+ if (typeof v === 'number') {
33
+ return Number.isFinite(v) ? v : d;
34
+ }
35
+ if (typeof v !== 'string' || !JSON_NUMBER.test(v)) {
36
+ return d;
37
+ }
38
+ const n = Number(v);
39
+ return Number.isFinite(n) ? n : d;
40
+ }
41
+
42
+ function numC(
43
+ v: unknown,
44
+ d: number,
45
+ min: number,
46
+ max: number,
47
+ integer: boolean,
48
+ ): number {
49
+ const n = num(v, d);
50
+ return n >= min && n <= max && (!integer || Number.isInteger(n)) ? n : d;
51
+ }
52
+
53
+ /**
54
+ * LinkNode's schema-declared fields, for a clone. Generated from that
55
+ * schema; do not edit by hand.
56
+ *
57
+ * @internal
58
+ */
59
+ export function afterCloneLinkNode(node: LinkNode, prevNode: LinkNode): void {
60
+ node.__rel = prevNode.__rel;
61
+ node.__target = prevNode.__target;
62
+ node.__title = prevNode.__title;
63
+ node.__url = prevNode.__url;
64
+ }
65
+
66
+ /** LinkNode's generated implementations, for its `$config`. @internal */
67
+ export const GENERATED_LINK: GeneratedJSONFactory = fields => {
68
+ const LINK_FORMAT_GETTER = getterTableOf(fields, 'format') as {
69
+ readonly [key: string]:
70
+ | ''
71
+ | 'center'
72
+ | 'end'
73
+ | 'justify'
74
+ | 'left'
75
+ | 'right'
76
+ | 'start';
77
+ };
78
+
79
+ const LINK_FORMAT_SETTER = setterTableOf(fields, 'format') as {
80
+ readonly [key: string]: 0 | 1 | 2 | 3 | 4 | 5 | 6;
81
+ };
82
+
83
+ const LINK_FORMAT_SETTER_DEFAULT = setterDefaultOf(fields, 'format') as
84
+ | 0
85
+ | 1
86
+ | 2
87
+ | 3
88
+ | 4
89
+ | 5
90
+ | 6;
91
+
92
+ /** Generated from LinkNode's serialization schema. Do not edit by hand. */
93
+ function exportLinkNode(node: LinkNode): {[key: string]: unknown} {
94
+ const textFormat = node.__textFormat;
95
+ const textStyle = node.__textStyle;
96
+ const shouldSerializeTextStyles =
97
+ (textFormat !== 0 || textStyle !== '') &&
98
+ node.shouldSerializeTextStyles();
99
+ return {
100
+ children: [],
101
+ rel: node.__rel,
102
+ target: node.__target,
103
+ title: node.__title,
104
+ url: node.__url,
105
+ direction: node.__dir,
106
+ format: LINK_FORMAT_GETTER[node.__format],
107
+ indent: node.__indent,
108
+ textFormat:
109
+ textFormat !== 0 && shouldSerializeTextStyles ? textFormat : undefined,
110
+ textStyle:
111
+ textStyle !== '' && shouldSerializeTextStyles ? textStyle : undefined,
112
+ type: node.__type,
113
+ version: 1,
114
+ };
115
+ }
116
+
117
+ /** Generated from LinkNode's serialization schema. Do not edit by hand. */
118
+ function exportCompactLinkNode(node: LinkNode): {[key: string]: unknown} {
119
+ const textFormat = node.__textFormat;
120
+ const textStyle = node.__textStyle;
121
+ const shouldSerializeTextStyles =
122
+ (textFormat !== 0 || textStyle !== '') &&
123
+ node.shouldSerializeTextStyles();
124
+ const json: {[key: string]: unknown} = {type: node.__type, children: []};
125
+ const rel = node.__rel;
126
+ if (rel != null) {
127
+ json.rel = rel;
128
+ }
129
+ const target = node.__target;
130
+ if (target != null) {
131
+ json.target = target;
132
+ }
133
+ const title = node.__title;
134
+ if (title != null) {
135
+ json.title = title;
136
+ }
137
+ const url = node.__url;
138
+ if (url !== undefined && url !== '') {
139
+ json.url = url;
140
+ }
141
+ const direction = node.__dir;
142
+ if (direction != null) {
143
+ json.direction = direction;
144
+ }
145
+ const format = LINK_FORMAT_GETTER[node.__format];
146
+ if (format !== undefined && format !== '') {
147
+ json.format = format;
148
+ }
149
+ const indent = node.__indent;
150
+ if (indent !== undefined && indent !== 0) {
151
+ json.indent = indent;
152
+ }
153
+ if (
154
+ textFormat !== undefined &&
155
+ textFormat !== 0 &&
156
+ shouldSerializeTextStyles
157
+ ) {
158
+ json.textFormat = textFormat;
159
+ }
160
+ if (
161
+ textStyle !== undefined &&
162
+ textStyle !== '' &&
163
+ shouldSerializeTextStyles
164
+ ) {
165
+ json.textStyle = textStyle;
166
+ }
167
+ return json;
168
+ }
169
+
170
+ /** Generated from LinkNode's serialization schema. Do not edit by hand. */
171
+ function updateLinkNode(
172
+ node: LinkNode,
173
+ json: {readonly [key: string]: unknown},
174
+ ): LinkNode {
175
+ const direction = json.direction;
176
+ node.__dir =
177
+ direction === null || direction === 'ltr' || direction === 'rtl'
178
+ ? direction
179
+ : null;
180
+ const format = json.format;
181
+ node.__format =
182
+ typeof format === 'string' && format in LINK_FORMAT_SETTER
183
+ ? LINK_FORMAT_SETTER[format]
184
+ : LINK_FORMAT_SETTER_DEFAULT;
185
+ node.__indent = numC(json.indent, 0, 0, Infinity, true);
186
+ node.__textFormat = num(json.textFormat, 0);
187
+ const textStyle = json.textStyle;
188
+ node.__textStyle = typeof textStyle === 'string' ? textStyle : '';
189
+ const rel = json.rel;
190
+ node.__rel = typeof rel === 'string' && rel !== '' ? rel : null;
191
+ const target = json.target;
192
+ node.__target = typeof target === 'string' && target !== '' ? target : null;
193
+ const title = json.title;
194
+ node.__title = typeof title === 'string' && title !== '' ? title : null;
195
+ const url = json.url;
196
+ node.__url = typeof url === 'string' ? url : '';
197
+ return node;
198
+ }
199
+
200
+ return {
201
+ exportJSON: exportLinkNode,
202
+ exportCompactJSON: exportCompactLinkNode,
203
+ updateFromJSON: updateLinkNode,
204
+ afterCloneFrom: afterCloneLinkNode,
205
+ };
206
+ };
207
+
208
+ /**
209
+ * AutoLinkNode's schema-declared fields, for a clone. Generated from that
210
+ * schema; do not edit by hand.
211
+ *
212
+ * @internal
213
+ */
214
+ export function afterCloneAutoLinkNode(
215
+ node: AutoLinkNode,
216
+ prevNode: AutoLinkNode,
217
+ ): void {
218
+ node.__isUnlinked = prevNode.__isUnlinked;
219
+ }
220
+
221
+ /** AutoLinkNode's generated implementations, for its `$config`. @internal */
222
+ export const GENERATED_AUTOLINK: GeneratedJSONFactory = fields => {
223
+ const AUTOLINK_FORMAT_GETTER = getterTableOf(fields, 'format') as {
224
+ readonly [key: string]:
225
+ | ''
226
+ | 'center'
227
+ | 'end'
228
+ | 'justify'
229
+ | 'left'
230
+ | 'right'
231
+ | 'start';
232
+ };
233
+
234
+ const AUTOLINK_FORMAT_SETTER = setterTableOf(fields, 'format') as {
235
+ readonly [key: string]: 0 | 1 | 2 | 3 | 4 | 5 | 6;
236
+ };
237
+
238
+ const AUTOLINK_FORMAT_SETTER_DEFAULT = setterDefaultOf(fields, 'format') as
239
+ | 0
240
+ | 1
241
+ | 2
242
+ | 3
243
+ | 4
244
+ | 5
245
+ | 6;
246
+
247
+ /** Generated from AutoLinkNode's serialization schema. Do not edit by hand. */
248
+ function exportAutoLinkNode(node: AutoLinkNode): {[key: string]: unknown} {
249
+ const textFormat = node.__textFormat;
250
+ const textStyle = node.__textStyle;
251
+ const shouldSerializeTextStyles =
252
+ (textFormat !== 0 || textStyle !== '') &&
253
+ node.shouldSerializeTextStyles();
254
+ return {
255
+ children: [],
256
+ isUnlinked: node.__isUnlinked,
257
+ rel: node.__rel,
258
+ target: node.__target,
259
+ title: node.__title,
260
+ url: node.__url,
261
+ direction: node.__dir,
262
+ format: AUTOLINK_FORMAT_GETTER[node.__format],
263
+ indent: node.__indent,
264
+ textFormat:
265
+ textFormat !== 0 && shouldSerializeTextStyles ? textFormat : undefined,
266
+ textStyle:
267
+ textStyle !== '' && shouldSerializeTextStyles ? textStyle : undefined,
268
+ type: node.__type,
269
+ version: 1,
270
+ };
271
+ }
272
+
273
+ /** Generated from AutoLinkNode's serialization schema. Do not edit by hand. */
274
+ function exportCompactAutoLinkNode(node: AutoLinkNode): {
275
+ [key: string]: unknown;
276
+ } {
277
+ const textFormat = node.__textFormat;
278
+ const textStyle = node.__textStyle;
279
+ const shouldSerializeTextStyles =
280
+ (textFormat !== 0 || textStyle !== '') &&
281
+ node.shouldSerializeTextStyles();
282
+ const json: {[key: string]: unknown} = {type: node.__type, children: []};
283
+ const isUnlinked = node.__isUnlinked;
284
+ if (isUnlinked !== undefined && isUnlinked !== false) {
285
+ json.isUnlinked = isUnlinked;
286
+ }
287
+ const rel = node.__rel;
288
+ if (rel != null) {
289
+ json.rel = rel;
290
+ }
291
+ const target = node.__target;
292
+ if (target != null) {
293
+ json.target = target;
294
+ }
295
+ const title = node.__title;
296
+ if (title != null) {
297
+ json.title = title;
298
+ }
299
+ const url = node.__url;
300
+ if (url !== undefined && url !== '') {
301
+ json.url = url;
302
+ }
303
+ const direction = node.__dir;
304
+ if (direction != null) {
305
+ json.direction = direction;
306
+ }
307
+ const format = AUTOLINK_FORMAT_GETTER[node.__format];
308
+ if (format !== undefined && format !== '') {
309
+ json.format = format;
310
+ }
311
+ const indent = node.__indent;
312
+ if (indent !== undefined && indent !== 0) {
313
+ json.indent = indent;
314
+ }
315
+ if (
316
+ textFormat !== undefined &&
317
+ textFormat !== 0 &&
318
+ shouldSerializeTextStyles
319
+ ) {
320
+ json.textFormat = textFormat;
321
+ }
322
+ if (
323
+ textStyle !== undefined &&
324
+ textStyle !== '' &&
325
+ shouldSerializeTextStyles
326
+ ) {
327
+ json.textStyle = textStyle;
328
+ }
329
+ return json;
330
+ }
331
+
332
+ /** Generated from AutoLinkNode's serialization schema. Do not edit by hand. */
333
+ function updateAutoLinkNode(
334
+ node: AutoLinkNode,
335
+ json: {readonly [key: string]: unknown},
336
+ ): AutoLinkNode {
337
+ const direction = json.direction;
338
+ node.__dir =
339
+ direction === null || direction === 'ltr' || direction === 'rtl'
340
+ ? direction
341
+ : null;
342
+ const format = json.format;
343
+ node.__format =
344
+ typeof format === 'string' && format in AUTOLINK_FORMAT_SETTER
345
+ ? AUTOLINK_FORMAT_SETTER[format]
346
+ : AUTOLINK_FORMAT_SETTER_DEFAULT;
347
+ node.__indent = numC(json.indent, 0, 0, Infinity, true);
348
+ node.__textFormat = num(json.textFormat, 0);
349
+ const textStyle = json.textStyle;
350
+ node.__textStyle = typeof textStyle === 'string' ? textStyle : '';
351
+ const rel = json.rel;
352
+ node.__rel = typeof rel === 'string' && rel !== '' ? rel : null;
353
+ const target = json.target;
354
+ node.__target = typeof target === 'string' && target !== '' ? target : null;
355
+ const title = json.title;
356
+ node.__title = typeof title === 'string' && title !== '' ? title : null;
357
+ const url = json.url;
358
+ node.__url = typeof url === 'string' ? url : '';
359
+ const isUnlinked = json.isUnlinked;
360
+ node.__isUnlinked = typeof isUnlinked === 'boolean' ? isUnlinked : false;
361
+ return node;
362
+ }
363
+
364
+ return {
365
+ exportJSON: exportAutoLinkNode,
366
+ exportCompactJSON: exportCompactAutoLinkNode,
367
+ updateFromJSON: updateAutoLinkNode,
368
+ afterCloneFrom: afterCloneAutoLinkNode,
369
+ };
370
+ };
@@ -27,6 +27,7 @@ import {
27
27
  $setSelection,
28
28
  addClassNamesToElement,
29
29
  type BaseSelection,
30
+ booleanValue,
30
31
  createCommand,
31
32
  type DOMConversionOutput,
32
33
  type EditorConfig,
@@ -34,16 +35,23 @@ import {
34
35
  isHTMLAnchorElement,
35
36
  type LexicalCommand,
36
37
  type LexicalNode,
37
- type LexicalUpdateJSON,
38
+ type LexicalParseJSON,
38
39
  type NodeKey,
40
+ nodeSchema,
41
+ nullable,
39
42
  type Point,
40
43
  type PointCaret,
41
44
  type PointType,
42
45
  type RangeSelection,
43
46
  type SerializedElementNode,
47
+ type SerializedPartial,
44
48
  type Spread,
49
+ stringValue,
50
+ withField,
45
51
  } from 'lexical';
46
52
 
53
+ import {GENERATED_AUTOLINK, GENERATED_LINK} from './LexicalLinkGeneratedJSON';
54
+
47
55
  export type LinkAttributes = {
48
56
  rel?: null | string;
49
57
  target?: null | string;
@@ -71,7 +79,38 @@ const SUPPORTED_URL_PROTOCOLS = new Set([
71
79
  'tel:',
72
80
  ]);
73
81
 
82
+ const linkNodeSchema = nodeSchema<LinkNode>()({
83
+ // defaultAsNull preserves the legacy `value || null` semantics: an empty
84
+ // string (or junk that coerces to it) imports as null, not ''.
85
+ // Every property *is* its node field in both directions — each accessor is
86
+ // a bare read or write — so each is declared as the field it is. Only `url`
87
+ // names its accessors, because getURL/setURL are not the conventional
88
+ // getUrl/setUrl the others are resolved to.
89
+ rel: withField(nullable(stringValue(), {defaultAsNull: true}), {
90
+ field: '__rel',
91
+ }),
92
+ target: withField(nullable(stringValue(), {defaultAsNull: true}), {
93
+ field: '__target',
94
+ }),
95
+ title: withField(nullable(stringValue(), {defaultAsNull: true}), {
96
+ field: '__title',
97
+ }),
98
+ url: withField(stringValue(), {
99
+ field: '__url',
100
+ getter: 'getURL',
101
+ setter: 'setURL',
102
+ }),
103
+ });
104
+
105
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
106
+ export interface LinkNode {
107
+ exportJSON(compact?: false): SerializedLinkNode;
108
+ exportJSON(compact: boolean): SerializedPartial<SerializedLinkNode>;
109
+ updateFromJSON(serializedNode: LexicalParseJSON<SerializedLinkNode>): this;
110
+ }
111
+
74
112
  /** @noInheritDoc */
113
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
75
114
  export class LinkNode extends ElementNode {
76
115
  /** @internal */
77
116
  __url: string;
@@ -85,12 +124,14 @@ export class LinkNode extends ElementNode {
85
124
  $config() {
86
125
  return this.config('link', {
87
126
  extends: ElementNode,
127
+ generated: GENERATED_LINK,
88
128
  importDOM: {
89
129
  a: () => ({
90
130
  conversion: $convertAnchorElement,
91
131
  priority: 1,
92
132
  }),
93
133
  },
134
+ json: linkNodeSchema,
94
135
  });
95
136
  }
96
137
 
@@ -107,14 +148,6 @@ export class LinkNode extends ElementNode {
107
148
  this.__title = title;
108
149
  }
109
150
 
110
- afterCloneFrom(prevNode: this): void {
111
- super.afterCloneFrom(prevNode);
112
- this.__url = prevNode.__url;
113
- this.__rel = prevNode.__rel;
114
- this.__target = prevNode.__target;
115
- this.__title = prevNode.__title;
116
- }
117
-
118
151
  createDOM(config: EditorConfig): LinkHTMLElementType {
119
152
  const element = $getDocument().createElement('a');
120
153
  this.updateLinkDOM(null, element, config);
@@ -154,15 +187,6 @@ export class LinkNode extends ElementNode {
154
187
  return false;
155
188
  }
156
189
 
157
- updateFromJSON(serializedNode: LexicalUpdateJSON<SerializedLinkNode>): this {
158
- return super
159
- .updateFromJSON(serializedNode)
160
- .setURL(serializedNode.url)
161
- .setRel(serializedNode.rel || null)
162
- .setTarget(serializedNode.target || null)
163
- .setTitle(serializedNode.title || null);
164
- }
165
-
166
190
  sanitizeUrl(url: string): string {
167
191
  const rawUrl = url;
168
192
  url = formatUrl(url);
@@ -208,16 +232,6 @@ export class LinkNode extends ElementNode {
208
232
  return url;
209
233
  }
210
234
 
211
- exportJSON(): SerializedLinkNode | SerializedAutoLinkNode {
212
- return {
213
- ...super.exportJSON(),
214
- rel: this.getRel(),
215
- target: this.getTarget(),
216
- title: this.getTitle(),
217
- url: this.getURL(),
218
- };
219
- }
220
-
221
235
  getURL(): string {
222
236
  return this.getLatest().__url;
223
237
  }
@@ -482,8 +496,28 @@ export type SerializedAutoLinkNode = Spread<
482
496
  SerializedLinkNode
483
497
  >;
484
498
 
499
+ const autoLinkNodeSchema = nodeSchema<AutoLinkNode>()({
500
+ // The property *is* the field in both directions — both accessors are bare —
501
+ // so it is declared as the field it is. The conventional
502
+ // getIsUnlinked/setIsUnlinked are still deferred to, so a subclass that
503
+ // overrides either still decides.
504
+ isUnlinked: withField(booleanValue(), {
505
+ field: '__isUnlinked',
506
+ }),
507
+ });
508
+
509
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
510
+ export interface AutoLinkNode {
511
+ exportJSON(compact?: false): SerializedAutoLinkNode;
512
+ exportJSON(compact: boolean): SerializedPartial<SerializedAutoLinkNode>;
513
+ updateFromJSON(
514
+ serializedNode: LexicalParseJSON<SerializedAutoLinkNode>,
515
+ ): this;
516
+ }
517
+
485
518
  // Custom node type to override `canInsertTextAfter` that will
486
519
  // allow typing within the link
520
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
487
521
  export class AutoLinkNode extends LinkNode {
488
522
  /** @internal */
489
523
  /** Indicates whether the autolink was ever unlinked. **/
@@ -501,13 +535,12 @@ export class AutoLinkNode extends LinkNode {
501
535
  : false;
502
536
  }
503
537
 
504
- afterCloneFrom(prevNode: this): void {
505
- super.afterCloneFrom(prevNode);
506
- this.__isUnlinked = prevNode.__isUnlinked;
507
- }
508
-
509
538
  $config() {
510
- return this.config('autolink', {extends: LinkNode});
539
+ return this.config('autolink', {
540
+ extends: LinkNode,
541
+ generated: GENERATED_AUTOLINK,
542
+ json: autoLinkNodeSchema,
543
+ });
511
544
  }
512
545
 
513
546
  shouldMergeAdjacentLink(_otherLink: LinkNode): boolean {
@@ -543,21 +576,6 @@ export class AutoLinkNode extends LinkNode {
543
576
  );
544
577
  }
545
578
 
546
- updateFromJSON(
547
- serializedNode: LexicalUpdateJSON<SerializedAutoLinkNode>,
548
- ): this {
549
- return super
550
- .updateFromJSON(serializedNode)
551
- .setIsUnlinked(serializedNode.isUnlinked || false);
552
- }
553
-
554
- exportJSON(): SerializedAutoLinkNode {
555
- return {
556
- ...super.exportJSON(),
557
- isUnlinked: this.__isUnlinked,
558
- };
559
- }
560
-
561
579
  // insertNewAfter is deliberately not overridden: LinkNode's implementation
562
580
  // uses $copyNode, which runs clone() + afterCloneFrom() and so carries the
563
581
  // element props (format/indent/style/dir/textFormat/textStyle) and NodeState