@lexical/markdown 0.7.7 → 0.7.9

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.
@@ -20,20 +20,16 @@ var link = require('@lexical/link');
20
20
  * LICENSE file in the root directory of this source tree.
21
21
  *
22
22
  */
23
-
24
23
  function indexBy(list, callback) {
25
24
  const index = {};
26
-
27
25
  for (const item of list) {
28
26
  const key = callback(item);
29
-
30
27
  if (index[key]) {
31
28
  index[key].push(item);
32
29
  } else {
33
30
  index[key] = [item];
34
31
  }
35
32
  }
36
-
37
33
  return index;
38
34
  }
39
35
  function transformersByType(transformers) {
@@ -54,35 +50,30 @@ const PUNCTUATION_OR_SPACE = /[!-/:-@[-`{-~\s]/;
54
50
  *
55
51
  */
56
52
  function createMarkdownExport(transformers) {
57
- const byType = transformersByType(transformers); // Export only uses text formats that are responsible for single format
58
- // e.g. it will filter out *** (bold, italic) and instead use separate ** and *
53
+ const byType = transformersByType(transformers);
59
54
 
55
+ // Export only uses text formats that are responsible for single format
56
+ // e.g. it will filter out *** (bold, italic) and instead use separate ** and *
60
57
  const textFormatTransformers = byType.textFormat.filter(transformer => transformer.format.length === 1);
61
58
  return () => {
62
59
  const output = [];
63
60
  const children = lexical.$getRoot().getChildren();
64
-
65
61
  for (const child of children) {
66
62
  const result = exportTopLevelElements(child, byType.element, textFormatTransformers, byType.textMatch);
67
-
68
63
  if (result != null) {
69
64
  output.push(result);
70
65
  }
71
66
  }
72
-
73
67
  return output.join('\n\n');
74
68
  };
75
69
  }
76
-
77
70
  function exportTopLevelElements(node, elementTransformers, textTransformersIndex, textMatchTransformers) {
78
71
  for (const transformer of elementTransformers) {
79
72
  const result = transformer.export(node, _node => exportChildren(_node, textTransformersIndex, textMatchTransformers));
80
-
81
73
  if (result != null) {
82
74
  return result;
83
75
  }
84
76
  }
85
-
86
77
  if (lexical.$isElementNode(node)) {
87
78
  return exportChildren(node, textTransformersIndex, textMatchTransformers);
88
79
  } else if (lexical.$isDecoratorNode(node)) {
@@ -91,21 +82,17 @@ function exportTopLevelElements(node, elementTransformers, textTransformersIndex
91
82
  return null;
92
83
  }
93
84
  }
94
-
95
85
  function exportChildren(node, textTransformersIndex, textMatchTransformers) {
96
86
  const output = [];
97
87
  const children = node.getChildren();
98
-
99
88
  mainLoop: for (const child of children) {
100
89
  for (const transformer of textMatchTransformers) {
101
90
  const result = transformer.export(child, parentNode => exportChildren(parentNode, textTransformersIndex, textMatchTransformers), (textNode, textContent) => exportTextFormat(textNode, textContent, textTransformersIndex));
102
-
103
91
  if (result != null) {
104
92
  output.push(result);
105
93
  continue mainLoop;
106
94
  }
107
95
  }
108
-
109
96
  if (lexical.$isLineBreakNode(child)) {
110
97
  output.push('\n');
111
98
  } else if (lexical.$isTextNode(child)) {
@@ -116,10 +103,8 @@ function exportChildren(node, textTransformersIndex, textMatchTransformers) {
116
103
  output.push(child.getTextContent());
117
104
  }
118
105
  }
119
-
120
106
  return output.join('');
121
107
  }
122
-
123
108
  function exportTextFormat(node, textContent, textTransformers) {
124
109
  // This function handles the case of a string looking like this: " foo "
125
110
  // Where it would be invalid markdown to generate: "** foo **"
@@ -128,74 +113,61 @@ function exportTextFormat(node, textContent, textTransformers) {
128
113
  const frozenString = textContent.trim();
129
114
  let output = frozenString;
130
115
  const applied = new Set();
131
-
132
116
  for (const transformer of textTransformers) {
133
117
  const format = transformer.format[0];
134
118
  const tag = transformer.tag;
135
-
136
119
  if (hasFormat(node, format) && !applied.has(format)) {
137
120
  // Multiple tags might be used for the same format (*, _)
138
- applied.add(format); // Prevent adding opening tag is already opened by the previous sibling
139
-
121
+ applied.add(format);
122
+ // Prevent adding opening tag is already opened by the previous sibling
140
123
  const previousNode = getTextSibling(node, true);
141
-
142
124
  if (!hasFormat(previousNode, format)) {
143
125
  output = tag + output;
144
- } // Prevent adding closing tag if next sibling will do it
145
-
126
+ }
146
127
 
128
+ // Prevent adding closing tag if next sibling will do it
147
129
  const nextNode = getTextSibling(node, false);
148
-
149
130
  if (!hasFormat(nextNode, format)) {
150
131
  output += tag;
151
132
  }
152
133
  }
153
- } // Replace trimmed version of textContent ensuring surrounding whitespace is not modified
154
-
134
+ }
155
135
 
136
+ // Replace trimmed version of textContent ensuring surrounding whitespace is not modified
156
137
  return textContent.replace(frozenString, output);
157
- } // Get next or previous text sibling a text node, including cases
158
- // when it's a child of inline element (e.g. link)
159
-
138
+ }
160
139
 
140
+ // Get next or previous text sibling a text node, including cases
141
+ // when it's a child of inline element (e.g. link)
161
142
  function getTextSibling(node, backward) {
162
143
  let sibling = backward ? node.getPreviousSibling() : node.getNextSibling();
163
-
164
144
  if (!sibling) {
165
145
  const parent = node.getParentOrThrow();
166
-
167
146
  if (parent.isInline()) {
168
147
  sibling = backward ? parent.getPreviousSibling() : parent.getNextSibling();
169
148
  }
170
149
  }
171
-
172
150
  while (sibling) {
173
151
  if (lexical.$isElementNode(sibling)) {
174
152
  if (!sibling.isInline()) {
175
153
  break;
176
154
  }
177
-
178
155
  const descendant = backward ? sibling.getLastDescendant() : sibling.getFirstDescendant();
179
-
180
156
  if (lexical.$isTextNode(descendant)) {
181
157
  return descendant;
182
158
  } else {
183
159
  sibling = backward ? sibling.getPreviousSibling() : sibling.getNextSibling();
184
160
  }
185
161
  }
186
-
187
162
  if (lexical.$isTextNode(sibling)) {
188
163
  return sibling;
189
164
  }
190
-
191
165
  if (!lexical.$isElementNode(sibling)) {
192
166
  return null;
193
167
  }
194
168
  }
195
-
196
169
  return null;
197
170
  }
198
-
199
171
  function hasFormat(node, format) {
200
172
  return lexical.$isTextNode(node) && node.hasFormat(format);
201
173
  }
@@ -207,6 +179,7 @@ function hasFormat(node, format) {
207
179
  * LICENSE file in the root directory of this source tree.
208
180
  *
209
181
  */
182
+
210
183
  const CAN_USE_DOM = typeof window !== 'undefined' && typeof window.document !== 'undefined' && typeof window.document.createElement !== 'undefined';
211
184
 
212
185
  /**
@@ -221,10 +194,12 @@ CAN_USE_DOM && /Mac|iPod|iPhone|iPad/.test(navigator.platform);
221
194
  CAN_USE_DOM && /^(?!.*Seamonkey)(?=.*Firefox).*/i.test(navigator.userAgent);
222
195
  CAN_USE_DOM && 'InputEvent' in window && !documentMode ? 'getTargetRanges' in new window.InputEvent('input') : false;
223
196
  const IS_SAFARI = CAN_USE_DOM && /Version\/[\d.]+.*Safari/.test(navigator.userAgent);
224
- const IS_IOS = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; // Keep these in case we need to use them in the future.
225
- // export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
197
+ const IS_IOS = CAN_USE_DOM && /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
226
198
 
227
- CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent); // export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;
199
+ // Keep these in case we need to use them in the future.
200
+ // export const IS_WINDOWS: boolean = CAN_USE_DOM && /Win/.test(navigator.platform);
201
+ CAN_USE_DOM && /^(?=.*Chrome).*/i.test(navigator.userAgent);
202
+ // export const canUseTextInputEvent: boolean = CAN_USE_DOM && 'TextEvent' in window && !documentMode;
228
203
 
229
204
  /**
230
205
  * Copyright (c) Meta Platforms, Inc. and affiliates.
@@ -243,86 +218,72 @@ function createMarkdownImport(transformers) {
243
218
  const linesLength = lines.length;
244
219
  const root = lexical.$getRoot();
245
220
  root.clear();
246
-
247
221
  for (let i = 0; i < linesLength; i++) {
248
- const lineText = lines[i]; // Codeblocks are processed first as anything inside such block
222
+ const lineText = lines[i];
223
+ // Codeblocks are processed first as anything inside such block
249
224
  // is ignored for further processing
250
225
  // TODO:
251
226
  // Abstract it to be dynamic as other transformers (add multiline match option)
252
-
253
227
  const [codeBlockNode, shiftedIndex] = importCodeBlock(lines, i, root);
254
-
255
228
  if (codeBlockNode != null) {
256
229
  i = shiftedIndex;
257
230
  continue;
258
231
  }
259
-
260
232
  importBlocks(lineText, root, byType.element, textFormatTransformersIndex, byType.textMatch);
261
- } // Removing empty paragraphs as md does not really
262
- // allow empty lines and uses them as dilimiter
263
-
233
+ }
264
234
 
235
+ // Removing empty paragraphs as md does not really
236
+ // allow empty lines and uses them as dilimiter
265
237
  const children = root.getChildren();
266
-
267
238
  for (const child of children) {
268
239
  if (isEmptyParagraph(child)) {
269
240
  child.remove();
270
241
  }
271
242
  }
272
-
273
243
  root.selectEnd();
274
244
  };
275
245
  }
276
-
277
246
  function isEmptyParagraph(node) {
278
247
  if (!lexical.$isParagraphNode(node)) {
279
248
  return false;
280
249
  }
281
-
282
250
  const firstChild = node.getFirstChild();
283
251
  return firstChild == null || node.getChildrenSize() === 1 && lexical.$isTextNode(firstChild) && MARKDOWN_EMPTY_LINE_REG_EXP.test(firstChild.getTextContent());
284
252
  }
285
-
286
253
  function importBlocks(lineText, rootNode, elementTransformers, textFormatTransformersIndex, textMatchTransformers) {
287
254
  const lineTextTrimmed = lineText.trim();
288
255
  const textNode = lexical.$createTextNode(lineTextTrimmed);
289
256
  const elementNode = lexical.$createParagraphNode();
290
257
  elementNode.append(textNode);
291
258
  rootNode.append(elementNode);
292
-
293
259
  for (const {
294
260
  regExp,
295
261
  replace
296
262
  } of elementTransformers) {
297
263
  const match = lineText.match(regExp);
298
-
299
264
  if (match) {
300
265
  textNode.setTextContent(lineText.slice(match[0].length));
301
266
  replace(elementNode, [textNode], match, true);
302
267
  break;
303
268
  }
304
269
  }
270
+ importTextFormatTransformers(textNode, textFormatTransformersIndex, textMatchTransformers);
305
271
 
306
- importTextFormatTransformers(textNode, textFormatTransformersIndex, textMatchTransformers); // If no transformer found and we left with original paragraph node
272
+ // If no transformer found and we left with original paragraph node
307
273
  // can check if its content can be appended to the previous node
308
274
  // if it's a paragraph, quote or list
309
-
310
275
  if (elementNode.isAttached() && lineTextTrimmed.length > 0) {
311
276
  const previousNode = elementNode.getPreviousSibling();
312
-
313
277
  if (lexical.$isParagraphNode(previousNode) || richText.$isQuoteNode(previousNode) || list.$isListNode(previousNode)) {
314
278
  let targetNode = previousNode;
315
-
316
279
  if (list.$isListNode(previousNode)) {
317
280
  const lastDescendant = previousNode.getLastDescendant();
318
-
319
281
  if (lastDescendant == null) {
320
282
  targetNode = null;
321
283
  } else {
322
284
  targetNode = utils.$findMatchingParent(lastDescendant, list.$isListItemNode);
323
285
  }
324
286
  }
325
-
326
287
  if (targetNode != null && targetNode.getTextContentSize() > 0) {
327
288
  targetNode.splice(targetNode.getChildrenSize(), 0, [lexical.$createLineBreakNode(), ...elementNode.getChildren()]);
328
289
  elementNode.remove();
@@ -330,17 +291,13 @@ function importBlocks(lineText, rootNode, elementTransformers, textFormatTransfo
330
291
  }
331
292
  }
332
293
  }
333
-
334
294
  function importCodeBlock(lines, startLineIndex, rootNode) {
335
295
  const openMatch = lines[startLineIndex].match(CODE_BLOCK_REG_EXP);
336
-
337
296
  if (openMatch) {
338
297
  let endLineIndex = startLineIndex;
339
298
  const linesLength = lines.length;
340
-
341
299
  while (++endLineIndex < linesLength) {
342
300
  const closeMatch = lines[endLineIndex].match(CODE_BLOCK_REG_EXP);
343
-
344
301
  if (closeMatch) {
345
302
  const codeBlockNode = code.$createCodeNode(openMatch[1]);
346
303
  const textNode = lexical.$createTextNode(lines.slice(startLineIndex + 1, endLineIndex).join('\n'));
@@ -350,157 +307,133 @@ function importCodeBlock(lines, startLineIndex, rootNode) {
350
307
  }
351
308
  }
352
309
  }
353
-
354
310
  return [null, startLineIndex];
355
- } // Processing text content and replaces text format tags.
311
+ }
312
+
313
+ // Processing text content and replaces text format tags.
356
314
  // It takes outermost tag match and its content, creates text node with
357
315
  // format based on tag and then recursively executed over node's content
358
316
  //
359
317
  // E.g. for "*Hello **world**!*" string it will create text node with
360
318
  // "Hello **world**!" content and italic format and run recursively over
361
319
  // its content to transform "**world**" part
362
-
363
-
364
320
  function importTextFormatTransformers(textNode, textFormatTransformersIndex, textMatchTransformers) {
365
321
  const textContent = textNode.getTextContent();
366
322
  const match = findOutermostMatch(textContent, textFormatTransformersIndex);
367
-
368
323
  if (!match) {
369
324
  // Once text format processing is done run text match transformers, as it
370
325
  // only can span within single text node (unline formats that can cover multiple nodes)
371
326
  importTextMatchTransformers(textNode, textMatchTransformers);
372
327
  return;
373
328
  }
329
+ let currentNode, remainderNode, leadingNode;
374
330
 
375
- let currentNode, remainderNode, leadingNode; // If matching full content there's no need to run splitText and can reuse existing textNode
331
+ // If matching full content there's no need to run splitText and can reuse existing textNode
376
332
  // to update its content and apply format. E.g. for **_Hello_** string after applying bold
377
333
  // format (**) it will reuse the same text node to apply italic (_)
378
-
379
334
  if (match[0] === textContent) {
380
335
  currentNode = textNode;
381
336
  } else {
382
337
  const startIndex = match.index || 0;
383
338
  const endIndex = startIndex + match[0].length;
384
-
385
339
  if (startIndex === 0) {
386
340
  [currentNode, remainderNode] = textNode.splitText(endIndex);
387
341
  } else {
388
342
  [leadingNode, currentNode, remainderNode] = textNode.splitText(startIndex, endIndex);
389
343
  }
390
344
  }
391
-
392
345
  currentNode.setTextContent(match[2]);
393
346
  const transformer = textFormatTransformersIndex.transformersByTag[match[1]];
394
-
395
347
  if (transformer) {
396
348
  for (const format of transformer.format) {
397
349
  if (!currentNode.hasFormat(format)) {
398
350
  currentNode.toggleFormat(format);
399
351
  }
400
352
  }
401
- } // Recursively run over inner text if it's not inline code
402
-
353
+ }
403
354
 
355
+ // Recursively run over inner text if it's not inline code
404
356
  if (!currentNode.hasFormat('code')) {
405
357
  importTextFormatTransformers(currentNode, textFormatTransformersIndex, textMatchTransformers);
406
- } // Run over leading/remaining text if any
407
-
358
+ }
408
359
 
360
+ // Run over leading/remaining text if any
409
361
  if (leadingNode) {
410
362
  importTextFormatTransformers(leadingNode, textFormatTransformersIndex, textMatchTransformers);
411
363
  }
412
-
413
364
  if (remainderNode) {
414
365
  importTextFormatTransformers(remainderNode, textFormatTransformersIndex, textMatchTransformers);
415
366
  }
416
367
  }
417
-
418
368
  function importTextMatchTransformers(textNode_, textMatchTransformers) {
419
369
  let textNode = textNode_;
420
-
421
370
  mainLoop: while (textNode) {
422
371
  for (const transformer of textMatchTransformers) {
423
372
  const match = textNode.getTextContent().match(transformer.importRegExp);
424
-
425
373
  if (!match) {
426
374
  continue;
427
375
  }
428
-
429
376
  const startIndex = match.index || 0;
430
377
  const endIndex = startIndex + match[0].length;
431
378
  let replaceNode, leftTextNode, rightTextNode;
432
-
433
379
  if (startIndex === 0) {
434
380
  [replaceNode, textNode] = textNode.splitText(endIndex);
435
381
  } else {
436
382
  [leftTextNode, replaceNode, rightTextNode] = textNode.splitText(startIndex, endIndex);
437
383
  }
438
-
439
384
  if (leftTextNode) {
440
385
  importTextMatchTransformers(leftTextNode, textMatchTransformers);
441
386
  }
442
-
443
387
  if (rightTextNode) {
444
388
  textNode = rightTextNode;
445
389
  }
446
-
447
390
  transformer.replace(replaceNode, match);
448
391
  continue mainLoop;
449
392
  }
450
-
451
393
  break;
452
394
  }
453
- } // Finds first "<tag>content<tag>" match that is not nested into another tag
454
-
395
+ }
455
396
 
397
+ // Finds first "<tag>content<tag>" match that is not nested into another tag
456
398
  function findOutermostMatch(textContent, textTransformersIndex) {
457
399
  const openTagsMatch = textContent.match(textTransformersIndex.openTagsRegExp);
458
-
459
400
  if (openTagsMatch == null) {
460
401
  return null;
461
402
  }
462
-
463
403
  for (const match of openTagsMatch) {
464
404
  // Open tags reg exp might capture leading space so removing it
465
405
  // before using match to find transformer
466
406
  const tag = match.replace(/^\s/, '');
467
407
  const fullMatchRegExp = textTransformersIndex.fullMatchRegExpByTag[tag];
468
-
469
408
  if (fullMatchRegExp == null) {
470
409
  continue;
471
410
  }
472
-
473
411
  const fullMatch = textContent.match(fullMatchRegExp);
474
412
  const transformer = textTransformersIndex.transformersByTag[tag];
475
-
476
413
  if (fullMatch != null && transformer != null) {
477
414
  if (transformer.intraword !== false) {
478
415
  return fullMatch;
479
- } // For non-intraword transformers checking if it's within a word
480
- // or surrounded with space/punctuation/newline
481
-
416
+ }
482
417
 
418
+ // For non-intraword transformers checking if it's within a word
419
+ // or surrounded with space/punctuation/newline
483
420
  const {
484
421
  index = 0
485
422
  } = fullMatch;
486
423
  const beforeChar = textContent[index - 1];
487
424
  const afterChar = textContent[index + fullMatch[0].length];
488
-
489
425
  if ((!beforeChar || PUNCTUATION_OR_SPACE.test(beforeChar)) && (!afterChar || PUNCTUATION_OR_SPACE.test(afterChar))) {
490
426
  return fullMatch;
491
427
  }
492
428
  }
493
429
  }
494
-
495
430
  return null;
496
431
  }
497
-
498
432
  function createTextFormatTransformersIndex(textTransformers) {
499
433
  const transformersByTag = {};
500
434
  const fullMatchRegExpByTag = {};
501
435
  const openTagsRegExp = [];
502
436
  const escapeRegExp = `(?<![\\\\])`;
503
-
504
437
  for (const transformer of textTransformers) {
505
438
  const {
506
439
  tag
@@ -508,14 +441,12 @@ function createTextFormatTransformersIndex(textTransformers) {
508
441
  transformersByTag[tag] = transformer;
509
442
  const tagRegExp = tag.replace(/(\*|\^|\+)/g, '\\$1');
510
443
  openTagsRegExp.push(tagRegExp);
511
-
512
444
  if (IS_SAFARI || IS_IOS) {
513
445
  fullMatchRegExpByTag[tag] = new RegExp(`(${tagRegExp})(?![${tagRegExp}\\s])(.*?[^${tagRegExp}\\s])${tagRegExp}(?!${tagRegExp})`);
514
446
  } else {
515
447
  fullMatchRegExpByTag[tag] = new RegExp(`(?<![\\\\${tagRegExp}])(${tagRegExp})((\\\\${tagRegExp})?.*?[^${tagRegExp}\\s](\\\\${tagRegExp})?)((?<!\\\\)|(?<=\\\\\\\\))(${tagRegExp})(?![\\\\${tagRegExp}])`);
516
448
  }
517
449
  }
518
-
519
450
  return {
520
451
  // Reg exp to find open tag + content + close tag
521
452
  fullMatchRegExpByTag,
@@ -532,31 +463,27 @@ function createTextFormatTransformersIndex(textTransformers) {
532
463
  * LICENSE file in the root directory of this source tree.
533
464
  *
534
465
  */
535
-
536
466
  function runElementTransformers(parentNode, anchorNode, anchorOffset, elementTransformers) {
537
467
  const grandParentNode = parentNode.getParent();
538
-
539
468
  if (!lexical.$isRootOrShadowRoot(grandParentNode) || parentNode.getFirstChild() !== anchorNode) {
540
469
  return false;
541
470
  }
471
+ const textContent = anchorNode.getTextContent();
542
472
 
543
- const textContent = anchorNode.getTextContent(); // Checking for anchorOffset position to prevent any checks for cases when caret is too far
473
+ // Checking for anchorOffset position to prevent any checks for cases when caret is too far
544
474
  // from a line start to be a part of block-level markdown trigger.
545
475
  //
546
476
  // TODO:
547
477
  // Can have a quick check if caret is close enough to the beginning of the string (e.g. offset less than 10-20)
548
478
  // since otherwise it won't be a markdown shortcut, but tables are exception
549
-
550
479
  if (textContent[anchorOffset - 1] !== ' ') {
551
480
  return false;
552
481
  }
553
-
554
482
  for (const {
555
483
  regExp,
556
484
  replace
557
485
  } of elementTransformers) {
558
486
  const match = textContent.match(regExp);
559
-
560
487
  if (match && match[0].length === anchorOffset) {
561
488
  const nextSiblings = anchorNode.getNextSiblings();
562
489
  const [leadingNode, remainderNode] = anchorNode.splitText(anchorOffset);
@@ -566,131 +493,115 @@ function runElementTransformers(parentNode, anchorNode, anchorOffset, elementTra
566
493
  return true;
567
494
  }
568
495
  }
569
-
570
496
  return false;
571
497
  }
572
-
573
498
  function runTextMatchTransformers(anchorNode, anchorOffset, transformersByTrigger) {
574
499
  let textContent = anchorNode.getTextContent();
575
500
  const lastChar = textContent[anchorOffset - 1];
576
501
  const transformers = transformersByTrigger[lastChar];
577
-
578
502
  if (transformers == null) {
579
503
  return false;
580
- } // If typing in the middle of content, remove the tail to do
581
- // reg exp match up to a string end (caret position)
582
-
504
+ }
583
505
 
506
+ // If typing in the middle of content, remove the tail to do
507
+ // reg exp match up to a string end (caret position)
584
508
  if (anchorOffset < textContent.length) {
585
509
  textContent = textContent.slice(0, anchorOffset);
586
510
  }
587
-
588
511
  for (const transformer of transformers) {
589
512
  const match = textContent.match(transformer.regExp);
590
-
591
513
  if (match === null) {
592
514
  continue;
593
515
  }
594
-
595
516
  const startIndex = match.index || 0;
596
517
  const endIndex = startIndex + match[0].length;
597
518
  let replaceNode;
598
-
599
519
  if (startIndex === 0) {
600
520
  [replaceNode] = anchorNode.splitText(endIndex);
601
521
  } else {
602
522
  [, replaceNode] = anchorNode.splitText(startIndex, endIndex);
603
523
  }
604
-
605
524
  replaceNode.selectNext(0, 0);
606
525
  transformer.replace(replaceNode, match);
607
526
  return true;
608
527
  }
609
-
610
528
  return false;
611
529
  }
612
-
613
530
  function runTextFormatTransformers(anchorNode, anchorOffset, textFormatTransformers) {
614
531
  const textContent = anchorNode.getTextContent();
615
532
  const closeTagEndIndex = anchorOffset - 1;
616
- const closeChar = textContent[closeTagEndIndex]; // Quick check if we're possibly at the end of inline markdown style
617
-
533
+ const closeChar = textContent[closeTagEndIndex];
534
+ // Quick check if we're possibly at the end of inline markdown style
618
535
  const matchers = textFormatTransformers[closeChar];
619
-
620
536
  if (!matchers) {
621
537
  return false;
622
538
  }
623
-
624
539
  for (const matcher of matchers) {
625
540
  const {
626
541
  tag
627
542
  } = matcher;
628
543
  const tagLength = tag.length;
629
- const closeTagStartIndex = closeTagEndIndex - tagLength + 1; // If tag is not single char check if rest of it matches with text content
544
+ const closeTagStartIndex = closeTagEndIndex - tagLength + 1;
630
545
 
546
+ // If tag is not single char check if rest of it matches with text content
631
547
  if (tagLength > 1) {
632
548
  if (!isEqualSubString(textContent, closeTagStartIndex, tag, 0, tagLength)) {
633
549
  continue;
634
550
  }
635
- } // Space before closing tag cancels inline markdown
636
-
551
+ }
637
552
 
553
+ // Space before closing tag cancels inline markdown
638
554
  if (textContent[closeTagStartIndex - 1] === ' ') {
639
555
  continue;
640
- } // Some tags can not be used within words, hence should have newline/space/punctuation after it
641
-
556
+ }
642
557
 
558
+ // Some tags can not be used within words, hence should have newline/space/punctuation after it
643
559
  const afterCloseTagChar = textContent[closeTagEndIndex + 1];
644
-
645
560
  if (matcher.intraword === false && afterCloseTagChar && !PUNCTUATION_OR_SPACE.test(afterCloseTagChar)) {
646
561
  continue;
647
562
  }
648
-
649
563
  const closeNode = anchorNode;
650
564
  let openNode = closeNode;
651
- let openTagStartIndex = getOpenTagStartIndex(textContent, closeTagStartIndex, tag); // Go through text node siblings and search for opening tag
652
- // if haven't found it within the same text node as closing tag
565
+ let openTagStartIndex = getOpenTagStartIndex(textContent, closeTagStartIndex, tag);
653
566
 
567
+ // Go through text node siblings and search for opening tag
568
+ // if haven't found it within the same text node as closing tag
654
569
  let sibling = openNode;
655
-
656
570
  while (openTagStartIndex < 0 && (sibling = sibling.getPreviousSibling())) {
657
571
  if (lexical.$isLineBreakNode(sibling)) {
658
572
  break;
659
573
  }
660
-
661
574
  if (lexical.$isTextNode(sibling)) {
662
575
  const siblingTextContent = sibling.getTextContent();
663
576
  openNode = sibling;
664
577
  openTagStartIndex = getOpenTagStartIndex(siblingTextContent, siblingTextContent.length, tag);
665
578
  }
666
- } // Opening tag is not found
667
-
579
+ }
668
580
 
581
+ // Opening tag is not found
669
582
  if (openTagStartIndex < 0) {
670
583
  continue;
671
- } // No content between opening and closing tag
672
-
584
+ }
673
585
 
586
+ // No content between opening and closing tag
674
587
  if (openNode === closeNode && openTagStartIndex + tagLength === closeTagStartIndex) {
675
588
  continue;
676
- } // Checking longer tags for repeating chars (e.g. *** vs **)
677
-
589
+ }
678
590
 
591
+ // Checking longer tags for repeating chars (e.g. *** vs **)
679
592
  const prevOpenNodeText = openNode.getTextContent();
680
-
681
593
  if (openTagStartIndex > 0 && prevOpenNodeText[openTagStartIndex - 1] === closeChar) {
682
594
  continue;
683
- } // Some tags can not be used within words, hence should have newline/space/punctuation before it
684
-
595
+ }
685
596
 
597
+ // Some tags can not be used within words, hence should have newline/space/punctuation before it
686
598
  const beforeOpenTagChar = prevOpenNodeText[openTagStartIndex - 1];
687
-
688
599
  if (matcher.intraword === false && beforeOpenTagChar && !PUNCTUATION_OR_SPACE.test(beforeOpenTagChar)) {
689
600
  continue;
690
- } // Clean text from opening and closing tags (starting from closing tag
691
- // to prevent any offset shifts if we start from opening one)
692
-
601
+ }
693
602
 
603
+ // Clean text from opening and closing tags (starting from closing tag
604
+ // to prevent any offset shifts if we start from opening one)
694
605
  const prevCloseNodeText = closeNode.getTextContent();
695
606
  const closeNodeText = prevCloseNodeText.slice(0, closeTagStartIndex) + prevCloseNodeText.slice(closeTagEndIndex + 1);
696
607
  closeNode.setTextContent(closeNodeText);
@@ -698,62 +609,55 @@ function runTextFormatTransformers(anchorNode, anchorOffset, textFormatTransform
698
609
  openNode.setTextContent(openNodeText.slice(0, openTagStartIndex) + openNodeText.slice(openTagStartIndex + tagLength));
699
610
  const selection = lexical.$getSelection();
700
611
  const nextSelection = lexical.$createRangeSelection();
701
- lexical.$setSelection(nextSelection); // Adjust offset based on deleted chars
702
-
612
+ lexical.$setSelection(nextSelection);
613
+ // Adjust offset based on deleted chars
703
614
  const newOffset = closeTagEndIndex - tagLength * (openNode === closeNode ? 2 : 1) + 1;
704
615
  nextSelection.anchor.set(openNode.__key, openTagStartIndex, 'text');
705
- nextSelection.focus.set(closeNode.__key, newOffset, 'text'); // Apply formatting to selected text
616
+ nextSelection.focus.set(closeNode.__key, newOffset, 'text');
706
617
 
618
+ // Apply formatting to selected text
707
619
  for (const format of matcher.format) {
708
620
  if (!nextSelection.hasFormat(format)) {
709
621
  nextSelection.formatText(format);
710
622
  }
711
- } // Collapse selection up to the focus point
712
-
623
+ }
713
624
 
714
- nextSelection.anchor.set(nextSelection.focus.key, nextSelection.focus.offset, nextSelection.focus.type); // Remove formatting from collapsed selection
625
+ // Collapse selection up to the focus point
626
+ nextSelection.anchor.set(nextSelection.focus.key, nextSelection.focus.offset, nextSelection.focus.type);
715
627
 
628
+ // Remove formatting from collapsed selection
716
629
  for (const format of matcher.format) {
717
630
  if (nextSelection.hasFormat(format)) {
718
631
  nextSelection.toggleFormat(format);
719
632
  }
720
633
  }
721
-
722
634
  if (lexical.$isRangeSelection(selection)) {
723
635
  nextSelection.format = selection.format;
724
636
  }
725
-
726
637
  return true;
727
638
  }
728
-
729
639
  return false;
730
640
  }
731
-
732
641
  function getOpenTagStartIndex(string, maxIndex, tag) {
733
642
  const tagLength = tag.length;
734
-
735
643
  for (let i = maxIndex; i >= tagLength; i--) {
736
644
  const startIndex = i - tagLength;
737
-
738
- if (isEqualSubString(string, startIndex, tag, 0, tagLength) && // Space after opening tag cancels transformation
645
+ if (isEqualSubString(string, startIndex, tag, 0, tagLength) &&
646
+ // Space after opening tag cancels transformation
739
647
  string[startIndex + tagLength] !== ' ') {
740
648
  return startIndex;
741
649
  }
742
650
  }
743
-
744
651
  return -1;
745
652
  }
746
-
747
653
  function isEqualSubString(stringA, aStart, stringB, bStart, length) {
748
654
  for (let i = 0; i < length; i++) {
749
655
  if (stringA[aStart + i] !== stringB[bStart + i]) {
750
656
  return false;
751
657
  }
752
658
  }
753
-
754
659
  return true;
755
660
  }
756
-
757
661
  function registerMarkdownShortcuts(editor, transformers = TRANSFORMERS) {
758
662
  const byType = transformersByType(transformers);
759
663
  const textFormatTransformersIndex = indexBy(byType.textFormat, ({
@@ -762,13 +666,10 @@ function registerMarkdownShortcuts(editor, transformers = TRANSFORMERS) {
762
666
  const textMatchTransformersIndex = indexBy(byType.textMatch, ({
763
667
  trigger
764
668
  }) => trigger);
765
-
766
669
  for (const transformer of transformers) {
767
670
  const type = transformer.type;
768
-
769
671
  if (type === 'element' || type === 'text-match') {
770
672
  const dependencies = transformer.dependencies;
771
-
772
673
  if (!editor.hasNodes(dependencies)) {
773
674
  {
774
675
  throw Error(`MarkdownShortcuts: missing dependency for transformer. Ensure node dependency is included in editor initial config.`);
@@ -776,19 +677,15 @@ function registerMarkdownShortcuts(editor, transformers = TRANSFORMERS) {
776
677
  }
777
678
  }
778
679
  }
779
-
780
680
  const transform = (parentNode, anchorNode, anchorOffset) => {
781
681
  if (runElementTransformers(parentNode, anchorNode, anchorOffset, byType.element)) {
782
682
  return;
783
683
  }
784
-
785
684
  if (runTextMatchTransformers(anchorNode, anchorOffset, textMatchTransformersIndex)) {
786
685
  return;
787
686
  }
788
-
789
687
  runTextFormatTransformers(anchorNode, anchorOffset, textFormatTransformersIndex);
790
688
  };
791
-
792
689
  return editor.registerUpdateListener(({
793
690
  tags,
794
691
  dirtyLeaves,
@@ -799,35 +696,26 @@ function registerMarkdownShortcuts(editor, transformers = TRANSFORMERS) {
799
696
  if (tags.has('historic')) {
800
697
  return;
801
698
  }
802
-
803
699
  const selection = editorState.read(lexical.$getSelection);
804
700
  const prevSelection = prevEditorState.read(lexical.$getSelection);
805
-
806
701
  if (!lexical.$isRangeSelection(prevSelection) || !lexical.$isRangeSelection(selection) || !selection.isCollapsed()) {
807
702
  return;
808
703
  }
809
-
810
704
  const anchorKey = selection.anchor.key;
811
705
  const anchorOffset = selection.anchor.offset;
812
-
813
706
  const anchorNode = editorState._nodeMap.get(anchorKey);
814
-
815
707
  if (!lexical.$isTextNode(anchorNode) || !dirtyLeaves.has(anchorKey) || anchorOffset !== 1 && anchorOffset !== prevSelection.anchor.offset + 1) {
816
708
  return;
817
709
  }
818
-
819
710
  editor.update(() => {
820
711
  // Markdown is not available inside code
821
712
  if (anchorNode.hasFormat('code')) {
822
713
  return;
823
714
  }
824
-
825
715
  const parentNode = anchorNode.getParent();
826
-
827
716
  if (parentNode === null || code.$isCodeNode(parentNode)) {
828
717
  return;
829
718
  }
830
-
831
719
  transform(parentNode, anchorNode, selection.anchor.offset);
832
720
  });
833
721
  });
@@ -840,7 +728,6 @@ function registerMarkdownShortcuts(editor, transformers = TRANSFORMERS) {
840
728
  * LICENSE file in the root directory of this source tree.
841
729
  *
842
730
  */
843
-
844
731
  const createBlockNode = createNode => {
845
732
  return (parentNode, children, match) => {
846
733
  const node = createNode(match);
@@ -848,17 +735,15 @@ const createBlockNode = createNode => {
848
735
  parentNode.replace(node);
849
736
  node.select(0, 0);
850
737
  };
851
- }; // Amount of spaces that define indentation level
852
- // TODO: should be an option
853
-
738
+ };
854
739
 
740
+ // Amount of spaces that define indentation level
741
+ // TODO: should be an option
855
742
  const LIST_INDENT_SIZE = 4;
856
-
857
743
  const listReplace = listType => {
858
744
  return (parentNode, children, match) => {
859
745
  const previousNode = parentNode.getPreviousSibling();
860
746
  const listItem = list.$createListItemNode(listType === 'check' ? match[3] === 'x' : undefined);
861
-
862
747
  if (list.$isListNode(previousNode) && previousNode.getListType() === listType) {
863
748
  previousNode.append(listItem);
864
749
  parentNode.remove();
@@ -867,33 +752,27 @@ const listReplace = listType => {
867
752
  list$1.append(listItem);
868
753
  parentNode.replace(list$1);
869
754
  }
870
-
871
755
  listItem.append(...children);
872
756
  listItem.select(0, 0);
873
757
  const indent = Math.floor(match[1].length / LIST_INDENT_SIZE);
874
-
875
758
  if (indent) {
876
759
  listItem.setIndent(indent);
877
760
  }
878
761
  };
879
762
  };
880
-
881
763
  const listExport = (listNode, exportChildren, depth) => {
882
764
  const output = [];
883
765
  const children = listNode.getChildren();
884
766
  let index = 0;
885
-
886
767
  for (const listItemNode of children) {
887
768
  if (list.$isListItemNode(listItemNode)) {
888
769
  if (listItemNode.getChildrenSize() === 1) {
889
770
  const firstChild = listItemNode.getFirstChild();
890
-
891
771
  if (list.$isListNode(firstChild)) {
892
772
  output.push(listExport(firstChild, exportChildren, depth + 1));
893
773
  continue;
894
774
  }
895
775
  }
896
-
897
776
  const indent = ' '.repeat(depth * LIST_INDENT_SIZE);
898
777
  const listType = listNode.getListType();
899
778
  const prefix = listType === 'number' ? `${listNode.getStart() + index}. ` : listType === 'check' ? `- [${listItemNode.getChecked() ? 'x' : ' '}] ` : '- ';
@@ -901,17 +780,14 @@ const listExport = (listNode, exportChildren, depth) => {
901
780
  index++;
902
781
  }
903
782
  }
904
-
905
783
  return output.join('\n');
906
784
  };
907
-
908
785
  const HEADING = {
909
786
  dependencies: [richText.HeadingNode],
910
787
  export: (node, exportChildren) => {
911
788
  if (!richText.$isHeadingNode(node)) {
912
789
  return null;
913
790
  }
914
-
915
791
  const level = Number(node.getTag().slice(1));
916
792
  return '#'.repeat(level) + ' ' + exportChildren(node);
917
793
  },
@@ -928,21 +804,17 @@ const QUOTE = {
928
804
  if (!richText.$isQuoteNode(node)) {
929
805
  return null;
930
806
  }
931
-
932
807
  const lines = exportChildren(node).split('\n');
933
808
  const output = [];
934
-
935
809
  for (const line of lines) {
936
810
  output.push('> ' + line);
937
811
  }
938
-
939
812
  return output.join('\n');
940
813
  },
941
814
  regExp: /^>\s/,
942
815
  replace: (parentNode, children, _match, isImport) => {
943
816
  if (isImport) {
944
817
  const previousNode = parentNode.getPreviousSibling();
945
-
946
818
  if (richText.$isQuoteNode(previousNode)) {
947
819
  previousNode.splice(previousNode.getChildrenSize(), 0, [lexical.$createLineBreakNode(), ...children]);
948
820
  previousNode.select(0, 0);
@@ -950,7 +822,6 @@ const QUOTE = {
950
822
  return;
951
823
  }
952
824
  }
953
-
954
825
  const node = richText.$createQuoteNode();
955
826
  node.append(...children);
956
827
  parentNode.replace(node);
@@ -964,7 +835,6 @@ const CODE = {
964
835
  if (!code.$isCodeNode(node)) {
965
836
  return null;
966
837
  }
967
-
968
838
  const textContent = node.getTextContent();
969
839
  return '```' + (node.getLanguage() || '') + (textContent ? '\n' + textContent : '') + '\n' + '```';
970
840
  },
@@ -1006,6 +876,11 @@ const INLINE_CODE = {
1006
876
  tag: '`',
1007
877
  type: 'text-format'
1008
878
  };
879
+ const HIGHLIGHT = {
880
+ format: ['highlight'],
881
+ tag: '==',
882
+ type: 'text-format'
883
+ };
1009
884
  const BOLD_ITALIC_STAR = {
1010
885
  format: ['bold', 'italic'],
1011
886
  tag: '***',
@@ -1043,22 +918,22 @@ const ITALIC_UNDERSCORE = {
1043
918
  intraword: false,
1044
919
  tag: '_',
1045
920
  type: 'text-format'
1046
- }; // Order of text transformers matters:
921
+ };
922
+
923
+ // Order of text transformers matters:
1047
924
  //
1048
925
  // - code should go first as it prevents any transformations inside
1049
926
  // - then longer tags match (e.g. ** or __ should go before * or _)
1050
-
1051
927
  const LINK = {
1052
928
  dependencies: [link.LinkNode],
1053
929
  export: (node, exportChildren, exportFormat) => {
1054
930
  if (!link.$isLinkNode(node)) {
1055
931
  return null;
1056
932
  }
1057
-
1058
933
  const linkContent = `[${node.getTextContent()}](${node.getURL()})`;
1059
- const firstChild = node.getFirstChild(); // Add text styles only if link has single text node inside. If it's more
934
+ const firstChild = node.getFirstChild();
935
+ // Add text styles only if link has single text node inside. If it's more
1060
936
  // then one we ignore it as markdown does not support nested styles for links
1061
-
1062
937
  if (node.getChildrenSize() === 1 && lexical.$isTextNode(firstChild)) {
1063
938
  return exportFormat(firstChild, linkContent);
1064
939
  } else {
@@ -1080,20 +955,19 @@ const LINK = {
1080
955
  };
1081
956
 
1082
957
  /** @module @lexical/markdown */
1083
- const ELEMENT_TRANSFORMERS = [HEADING, QUOTE, CODE, UNORDERED_LIST, ORDERED_LIST]; // Order of text format transformers matters:
958
+ const ELEMENT_TRANSFORMERS = [HEADING, QUOTE, CODE, UNORDERED_LIST, ORDERED_LIST];
959
+
960
+ // Order of text format transformers matters:
1084
961
  //
1085
962
  // - code should go first as it prevents any transformations inside
1086
963
  // - then longer tags match (e.g. ** or __ should go before * or _)
1087
-
1088
- const TEXT_FORMAT_TRANSFORMERS = [INLINE_CODE, BOLD_ITALIC_STAR, BOLD_ITALIC_UNDERSCORE, BOLD_STAR, BOLD_UNDERSCORE, ITALIC_STAR, ITALIC_UNDERSCORE, STRIKETHROUGH];
964
+ const TEXT_FORMAT_TRANSFORMERS = [INLINE_CODE, BOLD_ITALIC_STAR, BOLD_ITALIC_UNDERSCORE, BOLD_STAR, BOLD_UNDERSCORE, HIGHLIGHT, ITALIC_STAR, ITALIC_UNDERSCORE, STRIKETHROUGH];
1089
965
  const TEXT_MATCH_TRANSFORMERS = [LINK];
1090
966
  const TRANSFORMERS = [...ELEMENT_TRANSFORMERS, ...TEXT_FORMAT_TRANSFORMERS, ...TEXT_MATCH_TRANSFORMERS];
1091
-
1092
967
  function $convertFromMarkdownString(markdown, transformers = TRANSFORMERS) {
1093
968
  const importMarkdown = createMarkdownImport(transformers);
1094
969
  return importMarkdown(markdown);
1095
970
  }
1096
-
1097
971
  function $convertToMarkdownString(transformers = TRANSFORMERS) {
1098
972
  const exportMarkdown = createMarkdownExport(transformers);
1099
973
  return exportMarkdown();
@@ -1109,6 +983,7 @@ exports.CHECK_LIST = CHECK_LIST;
1109
983
  exports.CODE = CODE;
1110
984
  exports.ELEMENT_TRANSFORMERS = ELEMENT_TRANSFORMERS;
1111
985
  exports.HEADING = HEADING;
986
+ exports.HIGHLIGHT = HIGHLIGHT;
1112
987
  exports.INLINE_CODE = INLINE_CODE;
1113
988
  exports.ITALIC_STAR = ITALIC_STAR;
1114
989
  exports.ITALIC_UNDERSCORE = ITALIC_UNDERSCORE;
@@ -23,12 +23,12 @@ let W=a=>(b,c,d)=>{d=a(d);d.append(...c);b.replace(d);d.select(0,0)},X=a=>(b,c,d
23
23
  (e=k.getFirstChild(),A.$isListNode(e))){d.push(Y(e,b,c+1));continue}e=" ".repeat(4*c);var l=a.getListType();l="number"===l?`${a.getStart()+f}. `:"check"===l?`- [${k.getChecked()?"x":" "}] `:"- ";d.push(e+l+b(k));f++}return d.join("\n")},ja={dependencies:[C.HeadingNode],export:(a,b)=>{if(!C.$isHeadingNode(a))return null;const c=Number(a.getTag().slice(1));return"#".repeat(c)+" "+b(a)},regExp:/^(#{1,6})\s/,replace:W(a=>C.$createHeadingNode("h"+a[1].length)),type:"element"},ka={dependencies:[C.QuoteNode],
24
24
  export:(a,b)=>{if(!C.$isQuoteNode(a))return null;a=b(a).split("\n");b=[];for(const c of a)b.push("> "+c);return b.join("\n")},regExp:/^>\s/,replace:(a,b,c,d)=>{if(d&&(c=a.getPreviousSibling(),C.$isQuoteNode(c))){c.splice(c.getChildrenSize(),0,[h.$createLineBreakNode(),...b]);c.select(0,0);a.remove();return}c=C.$createQuoteNode();c.append(...b);a.replace(c);c.select(0,0)},type:"element"},la={dependencies:[t.CodeNode],export:a=>{if(!t.$isCodeNode(a))return null;const b=a.getTextContent();return"```"+
25
25
  (a.getLanguage()||"")+(b?"\n"+b:"")+"\n```"},regExp:/^```(\w{1,10})?\s/,replace:W(a=>t.$createCodeNode(a?a[1]:void 0)),type:"element"},ma={dependencies:[A.ListNode,A.ListItemNode],export:(a,b)=>A.$isListNode(a)?Y(a,b,0):null,regExp:/^(\s*)[-*+]\s/,replace:X("bullet"),type:"element"},na={dependencies:[A.ListNode,A.ListItemNode],export:(a,b)=>A.$isListNode(a)?Y(a,b,0):null,regExp:/^(\s*)(?:-\s)?\s?(\[(\s|x)?\])\s/i,replace:X("check"),type:"element"},oa={dependencies:[A.ListNode,A.ListItemNode],export:(a,
26
- b)=>A.$isListNode(a)?Y(a,b,0):null,regExp:/^(\s*)(\d{1,})\.\s/,replace:X("number"),type:"element"},pa={format:["code"],tag:"`",type:"text-format"},qa={format:["bold","italic"],tag:"***",type:"text-format"},ra={format:["bold","italic"],intraword:!1,tag:"___",type:"text-format"},ta={format:["bold"],tag:"**",type:"text-format"},ua={format:["bold"],intraword:!1,tag:"__",type:"text-format"},va={format:["strikethrough"],tag:"~~",type:"text-format"},wa={format:["italic"],tag:"*",type:"text-format"},xa={format:["italic"],
27
- intraword:!1,tag:"_",type:"text-format"},ya={dependencies:[G.LinkNode],export:(a,b,c)=>{if(!G.$isLinkNode(a))return null;b=`[${a.getTextContent()}](${a.getURL()})`;const d=a.getFirstChild();return 1===a.getChildrenSize()&&h.$isTextNode(d)?c(d,b):b},importRegExp:/(?:\[([^[]+)\])(?:\(([^()]+)\))/,regExp:/(?:\[([^[]+)\])(?:\(([^()]+)\))$/,replace:(a,b)=>{const [,c,d]=b;b=G.$createLinkNode(d);const e=h.$createTextNode(c);e.setFormat(a.getFormat());b.append(e);a.replace(b)},trigger:")",type:"text-match"},
28
- za=[ja,ka,la,ma,oa],Aa=[pa,qa,ra,ta,ua,wa,xa,va],Ba=[ya],Z=[...za,...Aa,...Ba];exports.$convertFromMarkdownString=function(a,b=Z){return fa(b)(a)};exports.$convertToMarkdownString=function(a=Z){return ba(a)()};exports.BOLD_ITALIC_STAR=qa;exports.BOLD_ITALIC_UNDERSCORE=ra;exports.BOLD_STAR=ta;exports.BOLD_UNDERSCORE=ua;exports.CHECK_LIST=na;exports.CODE=la;exports.ELEMENT_TRANSFORMERS=za;exports.HEADING=ja;exports.INLINE_CODE=pa;exports.ITALIC_STAR=wa;exports.ITALIC_UNDERSCORE=xa;exports.LINK=ya;
29
- exports.ORDERED_LIST=oa;exports.QUOTE=ka;exports.STRIKETHROUGH=va;exports.TEXT_FORMAT_TRANSFORMERS=Aa;exports.TEXT_MATCH_TRANSFORMERS=Ba;exports.TRANSFORMERS=Z;exports.UNORDERED_LIST=ma;
26
+ b)=>A.$isListNode(a)?Y(a,b,0):null,regExp:/^(\s*)(\d{1,})\.\s/,replace:X("number"),type:"element"},pa={format:["code"],tag:"`",type:"text-format"},qa={format:["highlight"],tag:"==",type:"text-format"},ra={format:["bold","italic"],tag:"***",type:"text-format"},sa={format:["bold","italic"],intraword:!1,tag:"___",type:"text-format"},ua={format:["bold"],tag:"**",type:"text-format"},va={format:["bold"],intraword:!1,tag:"__",type:"text-format"},wa={format:["strikethrough"],tag:"~~",type:"text-format"},
27
+ xa={format:["italic"],tag:"*",type:"text-format"},ya={format:["italic"],intraword:!1,tag:"_",type:"text-format"},za={dependencies:[G.LinkNode],export:(a,b,c)=>{if(!G.$isLinkNode(a))return null;b=`[${a.getTextContent()}](${a.getURL()})`;const d=a.getFirstChild();return 1===a.getChildrenSize()&&h.$isTextNode(d)?c(d,b):b},importRegExp:/(?:\[([^[]+)\])(?:\(([^()]+)\))/,regExp:/(?:\[([^[]+)\])(?:\(([^()]+)\))$/,replace:(a,b)=>{const [,c,d]=b;b=G.$createLinkNode(d);const e=h.$createTextNode(c);e.setFormat(a.getFormat());
28
+ b.append(e);a.replace(b)},trigger:")",type:"text-match"},Aa=[ja,ka,la,ma,oa],Ba=[pa,ra,sa,ua,va,qa,xa,ya,wa],Ca=[za],Z=[...Aa,...Ba,...Ca];exports.$convertFromMarkdownString=function(a,b=Z){return fa(b)(a)};exports.$convertToMarkdownString=function(a=Z){return ba(a)()};exports.BOLD_ITALIC_STAR=ra;exports.BOLD_ITALIC_UNDERSCORE=sa;exports.BOLD_STAR=ua;exports.BOLD_UNDERSCORE=va;exports.CHECK_LIST=na;exports.CODE=la;exports.ELEMENT_TRANSFORMERS=Aa;exports.HEADING=ja;exports.HIGHLIGHT=qa;
29
+ exports.INLINE_CODE=pa;exports.ITALIC_STAR=xa;exports.ITALIC_UNDERSCORE=ya;exports.LINK=za;exports.ORDERED_LIST=oa;exports.QUOTE=ka;exports.STRIKETHROUGH=wa;exports.TEXT_FORMAT_TRANSFORMERS=Ba;exports.TEXT_MATCH_TRANSFORMERS=Ca;exports.TRANSFORMERS=Z;exports.UNORDERED_LIST=ma;
30
30
  exports.registerMarkdownShortcuts=function(a,b=Z){let c=I(b),d=H(c.textFormat,({tag:f})=>f[f.length-1]),e=H(c.textMatch,({trigger:f})=>f);for(let f of b)if(b=f.type,("element"===b||"text-match"===b)&&!a.hasNodes(f.dependencies))throw Error("Minified Lexical error #79; visit https://lexical.dev/docs/error?code=79 for the full message or use the non-minified dev environment for full errors and additional helpful warnings.");return a.registerUpdateListener(({tags:f,dirtyLeaves:l,editorState:k,prevEditorState:p})=>
31
31
  {if(!f.has("historic")){var r=k.read(h.$getSelection);f=p.read(h.$getSelection);if(h.$isRangeSelection(f)&&h.$isRangeSelection(r)&&r.isCollapsed()){p=r.anchor.key;var y=r.anchor.offset,q=k._nodeMap.get(p);h.$isTextNode(q)&&l.has(p)&&(1===y||y===f.anchor.offset+1)&&a.update(()=>{if(!q.hasFormat("code")){var m=q.getParent();if(null!==m&&!t.$isCodeNode(m)){var w=r.anchor.offset;b:{var g=c.element,n=m.getParent();if(h.$isRootOrShadowRoot(n)&&m.getFirstChild()===q&&(n=q.getTextContent()," "===n[w-1]))for(let {regExp:D,
32
- replace:E}of g)if((g=n.match(D))&&g[0].length===w){n=q.getNextSiblings();let [F,sa]=q.splitText(w);F.remove();n=sa?[sa,...n]:n;E(m,n,g,!1);m=!0;break b}m=!1}if(!m){b:{g=q.getTextContent();m=e[g[w-1]];if(null!=m){w<g.length&&(g=g.slice(0,w));for(x of m)if(m=g.match(x.regExp),null!==m){g=m.index||0;n=g+m[0].length;var v=void 0;0===g?[v]=q.splitText(n):[,v]=q.splitText(g,n);v.selectNext(0,0);x.replace(v,m);var x=!0;break b}}x=!1}if(!x)b:{n=q.getTextContent();--w;var u=n[w];if(x=d[u])for(let D of x){var {tag:B}=
32
+ replace:E}of g)if((g=n.match(D))&&g[0].length===w){n=q.getNextSiblings();let [F,ta]=q.splitText(w);F.remove();n=ta?[ta,...n]:n;E(m,n,g,!1);m=!0;break b}m=!1}if(!m){b:{g=q.getTextContent();m=e[g[w-1]];if(null!=m){w<g.length&&(g=g.slice(0,w));for(x of m)if(m=g.match(x.regExp),null!==m){g=m.index||0;n=g+m[0].length;var v=void 0;0===g?[v]=q.splitText(n):[,v]=q.splitText(g,n);v.selectNext(0,0);x.replace(v,m);var x=!0;break b}}x=!1}if(!x)b:{n=q.getTextContent();--w;var u=n[w];if(x=d[u])for(let D of x){var {tag:B}=
33
33
  D;x=B.length;let E=w-x+1;if(!(1<x&&!V(n,E,B,0,x)||" "===n[E-1])&&(v=n[w+1],!1!==D.intraword||!v||J.test(v))){m=v=q;g=U(n,E,B);for(var z=m;0>g&&(z=z.getPreviousSibling())&&!h.$isLineBreakNode(z);)h.$isTextNode(z)&&(g=z.getTextContent(),m=z,g=U(g,g.length,B));if(!(0>g||m===v&&g+x===E||(B=m.getTextContent(),0<g&&B[g-1]===u||(z=B[g-1],!1===D.intraword&&z&&!J.test(z))))){n=v.getTextContent();n=n.slice(0,E)+n.slice(w+1);v.setTextContent(n);n=m===v?n:B;m.setTextContent(n.slice(0,g)+n.slice(g+x));n=h.$getSelection();
34
34
  u=h.$createRangeSelection();h.$setSelection(u);w=w-x*(m===v?2:1)+1;u.anchor.set(m.__key,g,"text");u.focus.set(v.__key,w,"text");for(let F of D.format)u.hasFormat(F)||u.formatText(F);u.anchor.set(u.focus.key,u.focus.offset,u.focus.type);for(let F of D.format)u.hasFormat(F)&&u.toggleFormat(F);h.$isRangeSelection(n)&&(u.format=n.format);break b}}}}}}}})}}})}
@@ -36,6 +36,7 @@ export declare const UNORDERED_LIST: ElementTransformer;
36
36
  export declare const CHECK_LIST: ElementTransformer;
37
37
  export declare const ORDERED_LIST: ElementTransformer;
38
38
  export declare const INLINE_CODE: TextFormatTransformer;
39
+ export declare const HIGHLIGHT: TextFormatTransformer;
39
40
  export declare const BOLD_ITALIC_STAR: TextFormatTransformer;
40
41
  export declare const BOLD_ITALIC_UNDERSCORE: TextFormatTransformer;
41
42
  export declare const BOLD_STAR: TextFormatTransformer;
package/index.d.ts CHANGED
@@ -8,11 +8,11 @@
8
8
  */
9
9
  import type { ElementTransformer, TextFormatTransformer, TextMatchTransformer, Transformer } from './MarkdownTransformers';
10
10
  import { registerMarkdownShortcuts } from './MarkdownShortcuts';
11
- import { BOLD_ITALIC_STAR, BOLD_ITALIC_UNDERSCORE, BOLD_STAR, BOLD_UNDERSCORE, CHECK_LIST, CODE, HEADING, INLINE_CODE, ITALIC_STAR, ITALIC_UNDERSCORE, LINK, ORDERED_LIST, QUOTE, STRIKETHROUGH, UNORDERED_LIST } from './MarkdownTransformers';
11
+ import { BOLD_ITALIC_STAR, BOLD_ITALIC_UNDERSCORE, BOLD_STAR, BOLD_UNDERSCORE, CHECK_LIST, CODE, HEADING, HIGHLIGHT, INLINE_CODE, ITALIC_STAR, ITALIC_UNDERSCORE, LINK, ORDERED_LIST, QUOTE, STRIKETHROUGH, UNORDERED_LIST } from './MarkdownTransformers';
12
12
  declare const ELEMENT_TRANSFORMERS: Array<ElementTransformer>;
13
13
  declare const TEXT_FORMAT_TRANSFORMERS: Array<TextFormatTransformer>;
14
14
  declare const TEXT_MATCH_TRANSFORMERS: Array<TextMatchTransformer>;
15
15
  declare const TRANSFORMERS: Array<Transformer>;
16
16
  declare function $convertFromMarkdownString(markdown: string, transformers?: Array<Transformer>): void;
17
17
  declare function $convertToMarkdownString(transformers?: Array<Transformer>): string;
18
- export { $convertFromMarkdownString, $convertToMarkdownString, BOLD_ITALIC_STAR, BOLD_ITALIC_UNDERSCORE, BOLD_STAR, BOLD_UNDERSCORE, CHECK_LIST, CODE, ELEMENT_TRANSFORMERS, ElementTransformer, HEADING, INLINE_CODE, ITALIC_STAR, ITALIC_UNDERSCORE, LINK, ORDERED_LIST, QUOTE, registerMarkdownShortcuts, STRIKETHROUGH, TEXT_FORMAT_TRANSFORMERS, TEXT_MATCH_TRANSFORMERS, TextFormatTransformer, TextMatchTransformer, Transformer, TRANSFORMERS, UNORDERED_LIST, };
18
+ export { $convertFromMarkdownString, $convertToMarkdownString, BOLD_ITALIC_STAR, BOLD_ITALIC_UNDERSCORE, BOLD_STAR, BOLD_UNDERSCORE, CHECK_LIST, CODE, ELEMENT_TRANSFORMERS, ElementTransformer, HEADING, HIGHLIGHT, INLINE_CODE, ITALIC_STAR, ITALIC_UNDERSCORE, LINK, ORDERED_LIST, QUOTE, registerMarkdownShortcuts, STRIKETHROUGH, TEXT_FORMAT_TRANSFORMERS, TEXT_MATCH_TRANSFORMERS, TextFormatTransformer, TextMatchTransformer, Transformer, TRANSFORMERS, UNORDERED_LIST, };
package/package.json CHANGED
@@ -8,18 +8,18 @@
8
8
  "markdown"
9
9
  ],
10
10
  "license": "MIT",
11
- "version": "0.7.7",
11
+ "version": "0.7.9",
12
12
  "main": "LexicalMarkdown.js",
13
13
  "peerDependencies": {
14
- "lexical": "0.7.7"
14
+ "lexical": "0.7.9"
15
15
  },
16
16
  "dependencies": {
17
- "@lexical/utils": "0.7.7",
18
- "@lexical/code": "0.7.7",
19
- "@lexical/text": "0.7.7",
20
- "@lexical/rich-text": "0.7.7",
21
- "@lexical/list": "0.7.7",
22
- "@lexical/link": "0.7.7"
17
+ "@lexical/utils": "0.7.9",
18
+ "@lexical/code": "0.7.9",
19
+ "@lexical/text": "0.7.9",
20
+ "@lexical/rich-text": "0.7.9",
21
+ "@lexical/list": "0.7.9",
22
+ "@lexical/link": "0.7.9"
23
23
  },
24
24
  "repository": {
25
25
  "type": "git",