onchain-lexical-markdown 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/OnchainLexicalMarkdown.js +11 -0
- package/README.md +96 -0
- package/flow/OnchainLexicalMarkdown.js.flow +130 -0
- package/package.json +45 -0
- package/src/MarkdownExport.ts +362 -0
- package/src/MarkdownImport.ts +343 -0
- package/src/MarkdownShortcuts.ts +529 -0
- package/src/MarkdownTransformers.ts +631 -0
- package/src/__tests__/unit/LexicalMarkdown.test.ts +891 -0
- package/src/fromMarkdownString.ts +39 -0
- package/src/importTextFormatTransformer.ts +137 -0
- package/src/importTextMatchTransformer.ts +108 -0
- package/src/importTextTransformers.ts +142 -0
- package/src/index.ts +81 -0
- package/src/toMarkdownString.ts +25 -0
- package/src/transformer/const.ts +12 -0
- package/src/transformer/hr.ts +37 -0
- package/src/transformer/index.ts +97 -0
- package/src/transformer/instance.ts +73 -0
- package/src/transformer/levelBasedControl.ts +95 -0
- package/src/transformer/table.ts +182 -0
- package/src/transformer/utils.ts +21 -0
- package/src/utils.ts +462 -0
|
@@ -0,0 +1,891 @@
|
|
|
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
|
+
import {CodeNode} from '@lexical/code';
|
|
10
|
+
import {createHeadlessEditor} from '@lexical/headless';
|
|
11
|
+
import {$generateHtmlFromNodes, $generateNodesFromDOM} from '@lexical/html';
|
|
12
|
+
import {$createLinkNode, LinkNode} from '@lexical/link';
|
|
13
|
+
import {ListItemNode, ListNode} from '@lexical/list';
|
|
14
|
+
import {HeadingNode, QuoteNode} from '@lexical/rich-text';
|
|
15
|
+
import {$createTextNode, $getRoot, $insertNodes} from 'lexical';
|
|
16
|
+
import {$createInstanceCodeNode} from 'onchain-lexical-instance';
|
|
17
|
+
|
|
18
|
+
import {
|
|
19
|
+
$convertFromMarkdownString,
|
|
20
|
+
$convertToMarkdownString,
|
|
21
|
+
LINK,
|
|
22
|
+
TextMatchTransformer,
|
|
23
|
+
Transformer,
|
|
24
|
+
TRANSFORMERS,
|
|
25
|
+
} from '../..';
|
|
26
|
+
import {
|
|
27
|
+
CODE,
|
|
28
|
+
MultilineElementTransformer,
|
|
29
|
+
normalizeMarkdown,
|
|
30
|
+
} from '../../MarkdownTransformers';
|
|
31
|
+
|
|
32
|
+
const SIMPLE_INLINE_JSX_MATCHER: TextMatchTransformer = {
|
|
33
|
+
dependencies: [LinkNode],
|
|
34
|
+
getEndIndex(node, match) {
|
|
35
|
+
// Find the closing tag. Count the number of opening and closing tags to find the correct closing tag.
|
|
36
|
+
// For simplicity, this will only count the opening and closing tags without checking for "MyTag" specifically.
|
|
37
|
+
let openedSubStartMatches = 0;
|
|
38
|
+
const start = (match.index ?? 0) + match[0].length;
|
|
39
|
+
let endIndex = start;
|
|
40
|
+
const line = node.getTextContent();
|
|
41
|
+
|
|
42
|
+
for (let i = start; i < line.length; i++) {
|
|
43
|
+
const char = line[i];
|
|
44
|
+
if (char === '<') {
|
|
45
|
+
const nextChar = line[i + 1];
|
|
46
|
+
if (nextChar === '/') {
|
|
47
|
+
if (openedSubStartMatches === 0) {
|
|
48
|
+
endIndex = i + '</MyTag>'.length;
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
openedSubStartMatches--;
|
|
52
|
+
} else {
|
|
53
|
+
openedSubStartMatches++;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return endIndex;
|
|
58
|
+
},
|
|
59
|
+
importRegExp: /<(MyTag)\s*>/,
|
|
60
|
+
regExp: /__ignore__/,
|
|
61
|
+
replace: (textNode, match) => {
|
|
62
|
+
const linkNode = $createLinkNode('simple-jsx');
|
|
63
|
+
|
|
64
|
+
const textStart = match[0].length + (match.index ?? 0);
|
|
65
|
+
const textEnd =
|
|
66
|
+
(match.index ?? 0) + textNode.getTextContent().length - '</MyTag>'.length;
|
|
67
|
+
const text = match.input?.slice(textStart, textEnd);
|
|
68
|
+
|
|
69
|
+
const linkTextNode = $createTextNode(text);
|
|
70
|
+
linkTextNode.setFormat(textNode.getFormat());
|
|
71
|
+
linkNode.append(linkTextNode);
|
|
72
|
+
textNode.replace(linkNode);
|
|
73
|
+
},
|
|
74
|
+
type: 'text-match',
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
// Matches html within a mdx file
|
|
78
|
+
const MDX_HTML_TRANSFORMER: MultilineElementTransformer = {
|
|
79
|
+
dependencies: [CodeNode],
|
|
80
|
+
export: (node) => {
|
|
81
|
+
if (node.getTextContent().startsWith('From HTML:')) {
|
|
82
|
+
return `<MyComponent>${node
|
|
83
|
+
.getTextContent()
|
|
84
|
+
.replace('From HTML: ', '')}</MyComponent>`;
|
|
85
|
+
}
|
|
86
|
+
return null; // Run next transformer
|
|
87
|
+
},
|
|
88
|
+
regExpEnd: /<\/(\w+)\s*>/,
|
|
89
|
+
regExpStart: /<(\w+)[^>]*>/,
|
|
90
|
+
replace: (rootNode, children, startMatch, endMatch, linesInBetween) => {
|
|
91
|
+
if (!linesInBetween) {
|
|
92
|
+
return false; // Run next transformer. We don't need to support markdown shortcuts for this test
|
|
93
|
+
}
|
|
94
|
+
if (startMatch[1] === 'MyComponent') {
|
|
95
|
+
const codeBlockNode = $createInstanceCodeNode(startMatch[1]);
|
|
96
|
+
const textNode = $createTextNode(
|
|
97
|
+
'From HTML: ' + linesInBetween.join('\n'),
|
|
98
|
+
);
|
|
99
|
+
codeBlockNode.append(textNode);
|
|
100
|
+
rootNode.append(codeBlockNode);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
return false; // Run next transformer
|
|
104
|
+
},
|
|
105
|
+
type: 'multiline-element',
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const CODE_TAG_COUNTER_EXAMPLE: MultilineElementTransformer = {
|
|
109
|
+
dependencies: CODE.dependencies,
|
|
110
|
+
export: CODE.export,
|
|
111
|
+
handleImportAfterStartMatch({lines, rootNode, startLineIndex, startMatch}) {
|
|
112
|
+
const regexpEndRegex: RegExp | undefined = /[ \t]*```$/;
|
|
113
|
+
|
|
114
|
+
const isEndOptional = false;
|
|
115
|
+
|
|
116
|
+
let endLineIndex = startLineIndex;
|
|
117
|
+
const linesLength = lines.length;
|
|
118
|
+
|
|
119
|
+
let openedSubStartMatches = 0;
|
|
120
|
+
|
|
121
|
+
// check every single line for the closing match. It could also be on the same line as the opening match.
|
|
122
|
+
while (endLineIndex < linesLength) {
|
|
123
|
+
const potentialSubStartMatch =
|
|
124
|
+
lines[endLineIndex].match(/^[ \t]*```(\w+)?/);
|
|
125
|
+
|
|
126
|
+
const endMatch = regexpEndRegex
|
|
127
|
+
? lines[endLineIndex].match(regexpEndRegex)
|
|
128
|
+
: null;
|
|
129
|
+
|
|
130
|
+
if (potentialSubStartMatch) {
|
|
131
|
+
if (endMatch) {
|
|
132
|
+
if ((potentialSubStartMatch.index ?? 0) < (endMatch.index ?? 0)) {
|
|
133
|
+
openedSubStartMatches++;
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
openedSubStartMatches++;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (endMatch) {
|
|
141
|
+
openedSubStartMatches--;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!endMatch || openedSubStartMatches > 0) {
|
|
145
|
+
if (
|
|
146
|
+
!isEndOptional ||
|
|
147
|
+
(isEndOptional && endLineIndex < linesLength - 1) // Optional end, but didn't reach the end of the document yet => continue searching for potential closing match
|
|
148
|
+
) {
|
|
149
|
+
endLineIndex++;
|
|
150
|
+
continue; // Search next line for closing match
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Now, check if the closing match matched is the same as the opening match.
|
|
155
|
+
// If it is, we need to continue searching for the actual closing match.
|
|
156
|
+
if (
|
|
157
|
+
endMatch &&
|
|
158
|
+
startLineIndex === endLineIndex &&
|
|
159
|
+
endMatch.index === startMatch.index
|
|
160
|
+
) {
|
|
161
|
+
endLineIndex++;
|
|
162
|
+
continue; // Search next line for closing match
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// At this point, we have found the closing match. Next: calculate the lines in between open and closing match
|
|
166
|
+
// This should not include the matches themselves, and be split up by lines
|
|
167
|
+
const linesInBetween: string[] = [];
|
|
168
|
+
|
|
169
|
+
if (endMatch && startLineIndex === endLineIndex) {
|
|
170
|
+
linesInBetween.push(
|
|
171
|
+
lines[startLineIndex].slice(
|
|
172
|
+
startMatch[0].length,
|
|
173
|
+
-endMatch[0].length,
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
} else {
|
|
177
|
+
for (let i = startLineIndex; i <= endLineIndex; i++) {
|
|
178
|
+
if (i === startLineIndex) {
|
|
179
|
+
const text = lines[i].slice(startMatch[0].length);
|
|
180
|
+
linesInBetween.push(text); // Also include empty text
|
|
181
|
+
} else if (i === endLineIndex && endMatch) {
|
|
182
|
+
const text = lines[i].slice(0, -endMatch[0].length);
|
|
183
|
+
linesInBetween.push(text); // Also include empty text
|
|
184
|
+
} else {
|
|
185
|
+
linesInBetween.push(lines[i]);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
CODE.replace(
|
|
192
|
+
rootNode,
|
|
193
|
+
null,
|
|
194
|
+
startMatch,
|
|
195
|
+
endMatch,
|
|
196
|
+
linesInBetween,
|
|
197
|
+
true,
|
|
198
|
+
) !== false
|
|
199
|
+
) {
|
|
200
|
+
// Return here. This $importMultiline function is run line by line and should only process a single multiline element at a time.
|
|
201
|
+
return [true, endLineIndex];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// The replace function returned false, despite finding the matching open and close tags => this transformer does not want to handle it.
|
|
205
|
+
// Thus, we continue letting the remaining transformers handle the passed lines of text from the beginning
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// No multiline transformer handled this line successfully
|
|
210
|
+
return [false, startLineIndex];
|
|
211
|
+
},
|
|
212
|
+
regExpStart: CODE.regExpStart,
|
|
213
|
+
replace: CODE.replace,
|
|
214
|
+
type: 'multiline-element',
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
describe('Markdown', () => {
|
|
218
|
+
type Input = Array<{
|
|
219
|
+
html: string;
|
|
220
|
+
md: string;
|
|
221
|
+
skipExport?: true;
|
|
222
|
+
skipImport?: true;
|
|
223
|
+
shouldPreserveNewLines?: true;
|
|
224
|
+
shouldMergeAdjacentLines?: true | false;
|
|
225
|
+
customTransformers?: Transformer[];
|
|
226
|
+
mdAfterExport?: string;
|
|
227
|
+
}>;
|
|
228
|
+
|
|
229
|
+
const URL = 'https://lexical.dev';
|
|
230
|
+
|
|
231
|
+
const IMPORT_AND_EXPORT: Input = [
|
|
232
|
+
{
|
|
233
|
+
html: '<h1><span style="white-space: pre-wrap;">Hello world</span></h1>',
|
|
234
|
+
md: '# Hello world',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
html: '<h2><span style="white-space: pre-wrap;">Hello world</span></h2>',
|
|
238
|
+
md: '## Hello world',
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
html: '<h3><span style="white-space: pre-wrap;">Hello world</span></h3>',
|
|
242
|
+
md: '### Hello world',
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
html: '<h4><span style="white-space: pre-wrap;">Hello world</span></h4>',
|
|
246
|
+
md: '#### Hello world',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
html: '<h5><span style="white-space: pre-wrap;">Hello world</span></h5>',
|
|
250
|
+
md: '##### Hello world',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
html: '<h6><span style="white-space: pre-wrap;">Hello world</span></h6>',
|
|
254
|
+
md: '###### Hello world',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
// Multiline paragraphs: https://spec.commonmark.org/dingus/?text=Hello%0Aworld%0A!
|
|
258
|
+
html: '<p><span style="white-space: pre-wrap;">Helloworld!</span></p>',
|
|
259
|
+
md: ['Hello', 'world', '!'].join('\n'),
|
|
260
|
+
shouldMergeAdjacentLines: true,
|
|
261
|
+
skipExport: true,
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
// Multiline paragraphs
|
|
265
|
+
// TO-DO: It would be nice to support also hard line breaks (<br>) as \ or double spaces
|
|
266
|
+
// See https://spec.commonmark.org/0.31.2/#hard-line-breaks.
|
|
267
|
+
// Example: '<p><span style="white-space: pre-wrap;">Hello\\\nworld\\\n!</span></p>',
|
|
268
|
+
html: '<p><span style="white-space: pre-wrap;">Hello<br>world<br>!</span></p>',
|
|
269
|
+
md: ['Hello', 'world', '!'].join('\n'),
|
|
270
|
+
skipImport: true,
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
html: '<blockquote><span style="white-space: pre-wrap;">Hello</span><br><span style="white-space: pre-wrap;">world!</span></blockquote>',
|
|
274
|
+
md: '> Hello\n> world!',
|
|
275
|
+
},
|
|
276
|
+
// TO-DO: <br> should be preserved
|
|
277
|
+
// {
|
|
278
|
+
// html: '<ul><li value="1"><span style="white-space: pre-wrap;">Hello</span></li><li value="2"><span style="white-space: pre-wrap;">world<br>!<br>!</span></li></ul>',
|
|
279
|
+
// md: '- Hello\n- world<br>!<br>!',
|
|
280
|
+
// skipImport: true,
|
|
281
|
+
// },
|
|
282
|
+
{
|
|
283
|
+
// Multiline list items: https://spec.commonmark.org/dingus/?text=-%20Hello%0A-%20world%0A!%0A!
|
|
284
|
+
html: '<ul><li value="1"><span style="white-space: pre-wrap;">Hello</span></li><li value="2"><span style="white-space: pre-wrap;">world!!</span></li></ul>',
|
|
285
|
+
md: '- Hello\n- world\n!\n!',
|
|
286
|
+
shouldMergeAdjacentLines: true,
|
|
287
|
+
skipExport: true,
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
html: '<ul><li value="1"><span style="white-space: pre-wrap;">Hello</span></li><li value="2"><span style="white-space: pre-wrap;">world</span></li></ul>',
|
|
291
|
+
md: '- Hello\n- world',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
html: '<ul><li value="1"><span style="white-space: pre-wrap;">Level 1</span></li><li value="2"><ul><li value="1"><span style="white-space: pre-wrap;">Level 2</span></li><li value="2"><ul><li value="1"><span style="white-space: pre-wrap;">Level 3</span></li></ul></li></ul></li></ul><p><span style="white-space: pre-wrap;">Hello world</span></p>',
|
|
295
|
+
md: '- Level 1\n - Level 2\n - Level 3\n\nHello world',
|
|
296
|
+
},
|
|
297
|
+
// List indentation with tabs, Import only: export will use " " only for one level of indentation
|
|
298
|
+
{
|
|
299
|
+
html: '<ul><li value="1"><span style="white-space: pre-wrap;">Level 1</span></li><li value="2"><ul><li value="1"><span style="white-space: pre-wrap;">Level 2</span></li><li value="2"><ul><li value="1"><span style="white-space: pre-wrap;">Level 3</span></li></ul></li></ul></li></ul><p><span style="white-space: pre-wrap;">Hello world</span></p>',
|
|
300
|
+
md: '- Level 1\n\t- Level 2\n \t - Level 3\n\nHello world',
|
|
301
|
+
skipExport: true,
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
// Import only: export will use "-" instead of "*"
|
|
305
|
+
html: '<ul><li value="1"><span style="white-space: pre-wrap;">Level 1</span></li><li value="2"><ul><li value="1"><span style="white-space: pre-wrap;">Level 2</span></li><li value="2"><ul><li value="1"><span style="white-space: pre-wrap;">Level 3</span></li></ul></li></ul></li></ul><p><span style="white-space: pre-wrap;">Hello world</span></p>',
|
|
306
|
+
md: '* Level 1\n * Level 2\n * Level 3\n\nHello world',
|
|
307
|
+
skipExport: true,
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
html: '<ol><li value="1"><span style="white-space: pre-wrap;">Hello</span></li><li value="2"><span style="white-space: pre-wrap;">world</span></li></ol>',
|
|
311
|
+
md: '1. Hello\n2. world',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
html: '<ol start="25"><li value="25"><span style="white-space: pre-wrap;">Hello</span></li><li value="26"><span style="white-space: pre-wrap;">world</span></li></ol>',
|
|
315
|
+
md: '25. Hello\n26. world',
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
html: '<p><i><em style="white-space: pre-wrap;">Hello</em></i><span style="white-space: pre-wrap;"> world</span></p>',
|
|
319
|
+
md: '*Hello* world',
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
html: '<p><b><strong style="white-space: pre-wrap;">Hello</strong></b><span style="white-space: pre-wrap;"> world</span></p>',
|
|
323
|
+
md: '**Hello** world',
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
html: '<p><i><b><strong style="white-space: pre-wrap;">Hello</strong></b></i><span style="white-space: pre-wrap;"> world</span></p>',
|
|
327
|
+
md: '***Hello*** world',
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
html: '<p><code spellcheck="false" style="white-space: pre-wrap;"><span>Hello</span></code><span style="white-space: pre-wrap;"> world</span></p>',
|
|
331
|
+
md: '`Hello` world',
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
html: '<p><s><span style="white-space: pre-wrap;">Hello</span></s><span style="white-space: pre-wrap;"> world</span></p>',
|
|
335
|
+
md: '~~Hello~~ world',
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
html: '<p><code spellcheck="false" style="white-space: pre-wrap;"><span>hello$</span></code></p>',
|
|
339
|
+
md: '`hello$`',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
html: '<p><code spellcheck="false" style="white-space: pre-wrap;"><span>$$hello</span></code></p>',
|
|
343
|
+
md: '`$$hello`',
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
html: '<p><a href="https://lexical.dev"><span style="white-space: pre-wrap;">Hello</span></a><span style="white-space: pre-wrap;"> world</span></p>',
|
|
347
|
+
md: '[Hello](https://lexical.dev) world',
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
html: '<p><a href="https://lexical.dev" title="Hello world"><span style="white-space: pre-wrap;">Hello</span></a><span style="white-space: pre-wrap;"> world</span></p>',
|
|
351
|
+
md: '[Hello](https://lexical.dev "Hello world") world',
|
|
352
|
+
},
|
|
353
|
+
{
|
|
354
|
+
html: '<p><a href="https://lexical.dev" title="Title with \\" escaped character"><span style="white-space: pre-wrap;">Hello</span></a><span style="white-space: pre-wrap;"> world</span></p>',
|
|
355
|
+
md: '[Hello](https://lexical.dev "Title with \\" escaped character") world',
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
html: '<p><span style="white-space: pre-wrap;">Hello </span><s><i><b><strong style="white-space: pre-wrap;">world</strong></b></i></s><span style="white-space: pre-wrap;">!</span></p>',
|
|
359
|
+
md: 'Hello ***~~world~~***!',
|
|
360
|
+
},
|
|
361
|
+
{
|
|
362
|
+
html: '<p><b><strong style="white-space: pre-wrap;">Hello </strong></b><s><b><strong style="white-space: pre-wrap;">world</strong></b></s><span style="white-space: pre-wrap;">!</span></p>',
|
|
363
|
+
md: '**Hello ~~world~~**!',
|
|
364
|
+
mdAfterExport: '**Hello ~~world~~**!',
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
html: '<p><s><b><strong style="white-space: pre-wrap;">Hello </strong></b></s><s><i><b><strong style="white-space: pre-wrap;">world</strong></b></i></s><s><span style="white-space: pre-wrap;">!</span></s></p>',
|
|
368
|
+
md: '**~~Hello *world*~~**~~!~~',
|
|
369
|
+
mdAfterExport: '**~~Hello *world*~~**~~!~~',
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
html: '<p><i><em style="white-space: pre-wrap;">Hello </em></i><i><b><strong style="white-space: pre-wrap;">world</strong></b></i><i><em style="white-space: pre-wrap;">!</em></i></p>',
|
|
373
|
+
md: '*Hello **world**!*',
|
|
374
|
+
mdAfterExport: '*Hello **world**!*',
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
html: '<p><span style="white-space: pre-wrap;">helloworld</span></p>',
|
|
378
|
+
md: 'hello\nworld',
|
|
379
|
+
shouldMergeAdjacentLines: true,
|
|
380
|
+
skipExport: true,
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
html: '<p><span style="white-space: pre-wrap;">hello</span><br><span style="white-space: pre-wrap;">world</span></p>',
|
|
384
|
+
md: 'hello\nworld',
|
|
385
|
+
shouldMergeAdjacentLines: false,
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
html: '<p><span style="white-space: pre-wrap;">hello</span></p><p><span style="white-space: pre-wrap;">world</span></p>',
|
|
389
|
+
md: 'hello\nworld',
|
|
390
|
+
shouldPreserveNewLines: true,
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
html: '<h1><span style="white-space: pre-wrap;">Hello</span></h1><p><br></p><p><br></p><p><br></p><p><b><strong style="white-space: pre-wrap;">world</strong></b><span style="white-space: pre-wrap;">!</span></p>',
|
|
394
|
+
md: '# Hello\n\n\n\n**world**!',
|
|
395
|
+
shouldPreserveNewLines: true,
|
|
396
|
+
},
|
|
397
|
+
{
|
|
398
|
+
html: '<h1><span style="white-space: pre-wrap;">Hello</span></h1><p><span style="white-space: pre-wrap;">hi</span></p><p><br></p><p><b><strong style="white-space: pre-wrap;">world</strong></b></p><p><br></p><p><span style="white-space: pre-wrap;">hi</span></p><blockquote><span style="white-space: pre-wrap;">hello</span><br><span style="white-space: pre-wrap;">hello</span></blockquote><p><br></p><h1><span style="white-space: pre-wrap;">hi</span></h1><p><br></p><p><span style="white-space: pre-wrap;">hi</span></p>',
|
|
399
|
+
md: '# Hello\nhi\n\n**world**\n\nhi\n> hello\n> hello\n\n# hi\n\nhi',
|
|
400
|
+
shouldPreserveNewLines: true,
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
// Import only: export will use * instead of _ due to registered transformers order
|
|
404
|
+
html: '<p><i><em style="white-space: pre-wrap;">Hello</em></i><span style="white-space: pre-wrap;"> world</span></p>',
|
|
405
|
+
md: '_Hello_ world',
|
|
406
|
+
skipExport: true,
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
// Import only: export will use * instead of _ due to registered transformers order
|
|
410
|
+
html: '<p><b><strong style="white-space: pre-wrap;">Hello</strong></b><span style="white-space: pre-wrap;"> world</span></p>',
|
|
411
|
+
md: '__Hello__ world',
|
|
412
|
+
skipExport: true,
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
// Import only: export will use * instead of _ due to registered transformers order
|
|
416
|
+
html: '<p><i><b><strong style="white-space: pre-wrap;">Hello</strong></b></i><span style="white-space: pre-wrap;"> world</span></p>',
|
|
417
|
+
md: '___Hello___ world',
|
|
418
|
+
skipExport: true,
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
// Import only: export will use * instead of _ due to registered transformers order
|
|
422
|
+
html: '<p><span style="white-space: pre-wrap;">Hello </span><s><i><b><strong style="white-space: pre-wrap;">world</strong></b></i></s><span style="white-space: pre-wrap;">!</span></p>',
|
|
423
|
+
md: 'Hello ~~__*world*__~~!',
|
|
424
|
+
skipExport: true,
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
html: '<pre spellcheck="false"><span style="white-space: pre-wrap;">Single line Code</span></pre>',
|
|
428
|
+
md: '```Single line Code```', // Ensure that "Single" is not read as the language by the code transformer. It should only be read as the language if there is a multi-line code block
|
|
429
|
+
skipExport: true, // Export will fail, as the code transformer will add new lines to the code block to make it multi-line. This is expected though, as the lexical code block is a block node and cannot be inline.
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
html: '<pre spellcheck="false" data-language="javascript" data-highlight-language="javascript"><span style="white-space: pre-wrap;">Incomplete tag</span></pre>',
|
|
433
|
+
md: '```javascript Incomplete tag',
|
|
434
|
+
skipExport: true,
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
html:
|
|
438
|
+
'<pre spellcheck="false" data-language="javascript" data-highlight-language="javascript"><span style="white-space: pre-wrap;">Incomplete multiline\n' +
|
|
439
|
+
'\n' +
|
|
440
|
+
'Tag</span></pre>',
|
|
441
|
+
md: '```javascript Incomplete multiline\n\nTag',
|
|
442
|
+
skipExport: true,
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
html: '<pre spellcheck="false"><span style="white-space: pre-wrap;">Code</span></pre>',
|
|
446
|
+
md: '```\nCode\n```',
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
html: '<pre spellcheck="false" data-language="javascript" data-highlight-language="javascript"><span style="white-space: pre-wrap;">Code</span></pre>',
|
|
450
|
+
md: '```javascript\nCode\n```',
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
// Should always preserve language in md but keep data-highlight-language only for supported languages
|
|
454
|
+
html: '<pre spellcheck="false" data-language="unknown"><span style="white-space: pre-wrap;">Code</span></pre>',
|
|
455
|
+
md: '```unknown\nCode\n```',
|
|
456
|
+
},
|
|
457
|
+
{
|
|
458
|
+
// Import only: prefix tabs will be removed for export
|
|
459
|
+
html: '<pre spellcheck="false"><span style="white-space: pre-wrap;">Code</span></pre>',
|
|
460
|
+
md: '\t```\nCode\n```',
|
|
461
|
+
skipExport: true,
|
|
462
|
+
},
|
|
463
|
+
{
|
|
464
|
+
// Import only: prefix spaces will be removed for export
|
|
465
|
+
html: '<pre spellcheck="false"><span style="white-space: pre-wrap;">Code</span></pre>',
|
|
466
|
+
md: ' ```\nCode\n```',
|
|
467
|
+
skipExport: true,
|
|
468
|
+
},
|
|
469
|
+
{
|
|
470
|
+
html: `<h3><span style="white-space: pre-wrap;">Code blocks</span></h3><pre spellcheck="false" data-language="javascript" data-highlight-language="javascript"><span style="white-space: pre-wrap;">1 + 1 = 2;</span></pre>`,
|
|
471
|
+
md: `### Code blocks
|
|
472
|
+
|
|
473
|
+
\`\`\`javascript
|
|
474
|
+
1 + 1 = 2;
|
|
475
|
+
\`\`\``,
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
// Import only: extra empty lines will be removed for export
|
|
479
|
+
html: '<p><span style="white-space: pre-wrap;">Hello</span></p><p><span style="white-space: pre-wrap;">world</span></p>',
|
|
480
|
+
md: ['Hello', '', '', '', 'world'].join('\n'),
|
|
481
|
+
skipExport: true,
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
// https://spec.commonmark.org/dingus/?text=%3E%20Hello%0Aworld%0A!
|
|
485
|
+
html: '<blockquote><span style="white-space: pre-wrap;">Helloworld!</span></blockquote>',
|
|
486
|
+
md: '> Hello\nworld\n!',
|
|
487
|
+
shouldMergeAdjacentLines: true,
|
|
488
|
+
skipExport: true,
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
// Import only: ensures that left side of splitText is processed for text match transformers
|
|
492
|
+
html: '<p><span style="white-space: pre-wrap;">Hello </span><a href="https://lexical.dev"><span style="white-space: pre-wrap;">world</span></a><span style="white-space: pre-wrap;">! Hello </span><mark style="white-space: pre-wrap;"><span>$world$</span></mark><span style="white-space: pre-wrap;">! </span><a href="https://lexical.dev"><span style="white-space: pre-wrap;">Hello</span></a><span style="white-space: pre-wrap;"> world! Hello </span><mark style="white-space: pre-wrap;"><span>$world$</span></mark><span style="white-space: pre-wrap;">!</span></p>',
|
|
493
|
+
md: `Hello [world](${URL})! Hello $world$! [Hello](${URL}) world! Hello $world$!`,
|
|
494
|
+
skipExport: true,
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
// Export only: import will use $...$ to transform <span /> to <mark /> due to HIGHLIGHT_TEXT_MATCH_IMPORT
|
|
498
|
+
html: "<p><span style='white-space: pre-wrap;'>$$H$&e$`l$'l$o$</span></p>",
|
|
499
|
+
md: "$$H$&e$\\`l$'l$o$",
|
|
500
|
+
skipImport: true,
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
customTransformers: [MDX_HTML_TRANSFORMER],
|
|
504
|
+
html: '<p><span style="white-space: pre-wrap;">Some HTML in mdx:</span></p><pre spellcheck="false" data-language="MyComponent"><span style="white-space: pre-wrap;">From HTML: Some Text</span></pre>',
|
|
505
|
+
md: 'Some HTML in mdx:\n\n<MyComponent>Some Text</MyComponent>',
|
|
506
|
+
shouldMergeAdjacentLines: true,
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
customTransformers: [MDX_HTML_TRANSFORMER],
|
|
510
|
+
html: '<p><span style="white-space: pre-wrap;">Some HTML in mdx:</span></p><pre spellcheck="false" data-language="MyComponent"><span style="white-space: pre-wrap;">From HTML: Line 1Some Text</span></pre>',
|
|
511
|
+
md: 'Some HTML in mdx:\n\n<MyComponent>Line 1\nSome Text</MyComponent>',
|
|
512
|
+
shouldMergeAdjacentLines: true,
|
|
513
|
+
skipExport: true,
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
customTransformers: [CODE_TAG_COUNTER_EXAMPLE],
|
|
517
|
+
// Ensure special ``` code block supports nested code blocks
|
|
518
|
+
html: '<pre spellcheck="false" data-language="ts" data-highlight-language="ts"><span style="white-space: pre-wrap;">Code\n```ts\nSub Code\n```</span></pre>',
|
|
519
|
+
md: '```ts\nCode\n```ts\nSub Code\n```\n```',
|
|
520
|
+
skipExport: true,
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
customTransformers: [SIMPLE_INLINE_JSX_MATCHER],
|
|
524
|
+
html: '<p><span style="white-space: pre-wrap;">Hello </span><a href="simple-jsx"><span style="white-space: pre-wrap;">One <MyTag>Two</MyTag></span></a><span style="white-space: pre-wrap;"> there</span></p>',
|
|
525
|
+
md: 'Hello <MyTag>One <MyTag>Two</MyTag></MyTag> there',
|
|
526
|
+
skipExport: true,
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
html: '<p><a href="https://lexical.dev"><span style="white-space: pre-wrap;">text</span></a></p>',
|
|
530
|
+
md: '[text](https://lexical.dev)',
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
html: '<p><code spellcheck="false" style="white-space: pre-wrap;"><span>text</span></code></p>',
|
|
534
|
+
md: '`text`',
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
html: '<p><a href="https://lexical.dev"><code spellcheck="false" style="white-space: pre-wrap;"><span>text</span></code></a></p>',
|
|
538
|
+
md: '[`text`](https://lexical.dev)',
|
|
539
|
+
},
|
|
540
|
+
{
|
|
541
|
+
html: '<p><b><strong style="white-space: pre-wrap;">Bold</strong></b><span style="white-space: pre-wrap;"> </span><a href="https://lexical.dev"><code spellcheck="false" style="white-space: pre-wrap;"><span>text</span></code></a><span style="white-space: pre-wrap;"> </span><b><strong style="white-space: pre-wrap;">Bold 2</strong></b></p>',
|
|
542
|
+
md: '**Bold** [`text`](https://lexical.dev) **Bold 2**',
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
html: '<p><b><strong style="white-space: pre-wrap;">Bold</strong></b><span style="white-space: pre-wrap;"> </span><a href="https://lexical.dev"><code spellcheck="false" style="white-space: pre-wrap;"><span>text</span></code><span style="white-space: pre-wrap;"> </span><b><strong style="white-space: pre-wrap;">Bold 2</strong></b></a><span style="white-space: pre-wrap;"> </span><b><strong style="white-space: pre-wrap;">Bold 3</strong></b></p>',
|
|
546
|
+
md: '**Bold** [`text` **Bold 2**](https://lexical.dev) **Bold 3**',
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
html: '<p><b><strong style="white-space: pre-wrap;">Bold</strong></b><span style="white-space: pre-wrap;"> </span><a href="https://lexical.dev"><code spellcheck="false" style="white-space: pre-wrap;"><span>text **Bold in code**</span></code></a><span style="white-space: pre-wrap;"> </span><b><strong style="white-space: pre-wrap;">Bold 3</strong></b></p>',
|
|
550
|
+
md: '**Bold** [`text **Bold in code**`](https://lexical.dev) **Bold 3**',
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
html: '<p><span style="white-space: pre-wrap;">Text </span><b><strong style="white-space: pre-wrap;">boldstart </strong></b><a href="https://lexical.dev"><b><strong style="white-space: pre-wrap;">text</strong></b></a><b><strong style="white-space: pre-wrap;"> boldend</strong></b><span style="white-space: pre-wrap;"> text</span></p>',
|
|
554
|
+
md: 'Text **boldstart [text](https://lexical.dev) boldend** text',
|
|
555
|
+
mdAfterExport:
|
|
556
|
+
'Text **boldstart [text](https://lexical.dev) boldend** text',
|
|
557
|
+
},
|
|
558
|
+
{
|
|
559
|
+
html: '<p><span style="white-space: pre-wrap;">Text </span><b><strong style="white-space: pre-wrap;">boldstart </strong></b><a href="https://lexical.dev"><b><code spellcheck="false" style="white-space: pre-wrap;"><strong>text</strong></code></b></a><b><strong style="white-space: pre-wrap;"> boldend</strong></b><span style="white-space: pre-wrap;"> text</span></p>',
|
|
560
|
+
md: 'Text **boldstart [`text`](https://lexical.dev) boldend** text',
|
|
561
|
+
mdAfterExport:
|
|
562
|
+
'Text **boldstart [`text`](https://lexical.dev) boldend** text',
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
html: '<p><span style="white-space: pre-wrap;">It </span><s><i><b><strong style="white-space: pre-wrap;">works </strong></b></i></s><a href="https://lexical.io"><s><i><b><strong style="white-space: pre-wrap;">with links</strong></b></i></s></a><span style="white-space: pre-wrap;"> too</span></p>',
|
|
566
|
+
md: 'It ~~___works [with links](https://lexical.io)___~~ too',
|
|
567
|
+
mdAfterExport:
|
|
568
|
+
'It ***~~works [with links](https://lexical.io)~~*** too',
|
|
569
|
+
},
|
|
570
|
+
{
|
|
571
|
+
html: '<p><span style="white-space: pre-wrap;">It </span><s><i><b><strong style="white-space: pre-wrap;">works </strong></b></i></s><a href="https://lexical.io"><s><i><b><strong style="white-space: pre-wrap;">with links</strong></b></i></s></a><s><i><b><strong style="white-space: pre-wrap;"> too</strong></b></i></s><span style="white-space: pre-wrap;">!</span></p>',
|
|
572
|
+
md: 'It ~~___works [with links](https://lexical.io) too___~~!',
|
|
573
|
+
mdAfterExport:
|
|
574
|
+
'It ***~~works [with links](https://lexical.io) too~~***!',
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
html: '<p><a href="https://lexical.dev"><span style="white-space: pre-wrap;">link</span></a><a href="https://lexical.dev"><span style="white-space: pre-wrap;">link2</span></a></p>',
|
|
578
|
+
md: '[link](https://lexical.dev)[link2](https://lexical.dev)',
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
html: '<p><b><code spellcheck="false" style="white-space: pre-wrap;"><strong>Bold Code</strong></code></b></p>',
|
|
582
|
+
md: '**`Bold Code`**',
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
html: '<p><span style="white-space: pre-wrap;">This is a backslash: \\</span></p>',
|
|
586
|
+
md: 'This is a backslash: \\\\',
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
html: '<p><span style="white-space: pre-wrap;">This is an asterisk: *</span></p>',
|
|
590
|
+
md: 'This is an asterisk: \\*',
|
|
591
|
+
},
|
|
592
|
+
{
|
|
593
|
+
html: '<p><span style="white-space: pre-wrap;">Backtick and asterisk: `**`</span></p>',
|
|
594
|
+
md: 'Backtick and asterisk: \\`\\*\\*\\`',
|
|
595
|
+
},
|
|
596
|
+
{
|
|
597
|
+
html: '<p><b><strong style="white-space: pre-wrap;">Backtick and asterisk: `**`</strong></b></p>',
|
|
598
|
+
md: '**Backtick and asterisk: \\`\\*\\*\\`**',
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
html: '<p><b><strong style="white-space: pre-wrap;">*test*</strong></b></p>',
|
|
602
|
+
md: '**\\*test\\***',
|
|
603
|
+
},
|
|
604
|
+
{
|
|
605
|
+
html: '<p><b><strong style="white-space: pre-wrap;">some bold text with an escaped star: *</strong></b><span style="white-space: pre-wrap;"> normal text</span></p>',
|
|
606
|
+
md: '**some bold text with an escaped star: \\*** normal text',
|
|
607
|
+
},
|
|
608
|
+
{
|
|
609
|
+
html: '<p><span style="white-space: pre-wrap;">*This text should </span><b><strong style="white-space: pre-wrap;">not</strong></b><span style="white-space: pre-wrap;"> be italic*</span></p>',
|
|
610
|
+
md: '\\*This text should **not** be italic*',
|
|
611
|
+
mdAfterExport: '\\*This text should **not** be italic\\*',
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
html: '<p><span style="white-space: pre-wrap;">*some text*</span></p>',
|
|
615
|
+
md: '\\*some text*',
|
|
616
|
+
mdAfterExport: '\\*some text\\*',
|
|
617
|
+
},
|
|
618
|
+
{
|
|
619
|
+
html: '<p><a href="https://lexical.dev"><span style="white-space: pre-wrap;">text </span><b><strong style="white-space: pre-wrap;">bold</strong></b><span style="white-space: pre-wrap;"> *normal*</span></a></p>',
|
|
620
|
+
md: '[text **bold** \\*normal\\*](https://lexical.dev)',
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
html: '<p><span style="white-space: pre-wrap;">*Hello* world</span></p>',
|
|
624
|
+
md: '\\*Hello\\* world',
|
|
625
|
+
},
|
|
626
|
+
{
|
|
627
|
+
html: '<p><b><strong style="white-space: pre-wrap;"> </strong></b></p>',
|
|
628
|
+
md: '** **',
|
|
629
|
+
},
|
|
630
|
+
];
|
|
631
|
+
|
|
632
|
+
const HIGHLIGHT_TEXT_MATCH_IMPORT: TextMatchTransformer = {
|
|
633
|
+
...LINK,
|
|
634
|
+
importRegExp: /\$([^$]+?)\$/,
|
|
635
|
+
replace: (textNode) => {
|
|
636
|
+
textNode.setFormat('highlight');
|
|
637
|
+
},
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
for (const {
|
|
641
|
+
html,
|
|
642
|
+
md,
|
|
643
|
+
skipImport,
|
|
644
|
+
shouldPreserveNewLines,
|
|
645
|
+
shouldMergeAdjacentLines,
|
|
646
|
+
customTransformers,
|
|
647
|
+
} of IMPORT_AND_EXPORT) {
|
|
648
|
+
if (skipImport) {
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
it(`can import "${md.replace(/\n/g, '\\n')}"`, () => {
|
|
653
|
+
const editor = createHeadlessEditor({
|
|
654
|
+
nodes: [
|
|
655
|
+
HeadingNode,
|
|
656
|
+
ListNode,
|
|
657
|
+
ListItemNode,
|
|
658
|
+
QuoteNode,
|
|
659
|
+
CodeNode,
|
|
660
|
+
LinkNode,
|
|
661
|
+
],
|
|
662
|
+
});
|
|
663
|
+
|
|
664
|
+
editor.update(
|
|
665
|
+
() =>
|
|
666
|
+
$convertFromMarkdownString(
|
|
667
|
+
md,
|
|
668
|
+
[
|
|
669
|
+
...(customTransformers || []),
|
|
670
|
+
...TRANSFORMERS,
|
|
671
|
+
HIGHLIGHT_TEXT_MATCH_IMPORT,
|
|
672
|
+
],
|
|
673
|
+
undefined,
|
|
674
|
+
shouldPreserveNewLines,
|
|
675
|
+
shouldMergeAdjacentLines,
|
|
676
|
+
),
|
|
677
|
+
{
|
|
678
|
+
discrete: true,
|
|
679
|
+
},
|
|
680
|
+
);
|
|
681
|
+
|
|
682
|
+
expect(
|
|
683
|
+
editor.getEditorState().read(() => $generateHtmlFromNodes(editor)),
|
|
684
|
+
).toBe(html);
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
for (const {
|
|
689
|
+
html,
|
|
690
|
+
md,
|
|
691
|
+
skipExport,
|
|
692
|
+
shouldPreserveNewLines,
|
|
693
|
+
customTransformers,
|
|
694
|
+
mdAfterExport,
|
|
695
|
+
} of IMPORT_AND_EXPORT) {
|
|
696
|
+
if (skipExport) {
|
|
697
|
+
continue;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
it(`can export "${md.replace(/\n/g, '\\n')}"`, () => {
|
|
701
|
+
const editor = createHeadlessEditor({
|
|
702
|
+
nodes: [
|
|
703
|
+
HeadingNode,
|
|
704
|
+
ListNode,
|
|
705
|
+
ListItemNode,
|
|
706
|
+
QuoteNode,
|
|
707
|
+
CodeNode,
|
|
708
|
+
LinkNode,
|
|
709
|
+
],
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
editor.update(
|
|
713
|
+
() => {
|
|
714
|
+
const parser = new DOMParser();
|
|
715
|
+
const dom = parser.parseFromString(html, 'text/html');
|
|
716
|
+
const nodes = $generateNodesFromDOM(editor, dom);
|
|
717
|
+
$getRoot().select();
|
|
718
|
+
$insertNodes(nodes);
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
discrete: true,
|
|
722
|
+
},
|
|
723
|
+
);
|
|
724
|
+
|
|
725
|
+
expect(
|
|
726
|
+
editor
|
|
727
|
+
.getEditorState()
|
|
728
|
+
.read(() =>
|
|
729
|
+
$convertToMarkdownString(
|
|
730
|
+
[...(customTransformers || []), ...TRANSFORMERS],
|
|
731
|
+
undefined,
|
|
732
|
+
shouldPreserveNewLines,
|
|
733
|
+
),
|
|
734
|
+
),
|
|
735
|
+
).toBe(mdAfterExport ?? md);
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
describe('normalizeMarkdown - shouldMergeAdjacentLines = true', () => {
|
|
741
|
+
it('should combine lines separated by a single \n unless they are in a codeblock', () => {
|
|
742
|
+
const markdown = `
|
|
743
|
+
A1
|
|
744
|
+
A2
|
|
745
|
+
|
|
746
|
+
A3
|
|
747
|
+
|
|
748
|
+
\`\`\`md
|
|
749
|
+
B1
|
|
750
|
+
B2
|
|
751
|
+
|
|
752
|
+
B3
|
|
753
|
+
\`\`\`
|
|
754
|
+
|
|
755
|
+
C1
|
|
756
|
+
C2
|
|
757
|
+
|
|
758
|
+
C3
|
|
759
|
+
|
|
760
|
+
\`\`\`js
|
|
761
|
+
D1
|
|
762
|
+
D2
|
|
763
|
+
|
|
764
|
+
D3
|
|
765
|
+
\`\`\`
|
|
766
|
+
|
|
767
|
+
\`\`\`single line code\`\`\`
|
|
768
|
+
|
|
769
|
+
E1
|
|
770
|
+
E2
|
|
771
|
+
|
|
772
|
+
E3
|
|
773
|
+
`;
|
|
774
|
+
expect(normalizeMarkdown(markdown, true)).toBe(`
|
|
775
|
+
A1A2
|
|
776
|
+
|
|
777
|
+
A3
|
|
778
|
+
|
|
779
|
+
\`\`\`md
|
|
780
|
+
B1
|
|
781
|
+
B2
|
|
782
|
+
|
|
783
|
+
B3
|
|
784
|
+
\`\`\`
|
|
785
|
+
|
|
786
|
+
C1C2
|
|
787
|
+
|
|
788
|
+
C3
|
|
789
|
+
|
|
790
|
+
\`\`\`js
|
|
791
|
+
D1
|
|
792
|
+
D2
|
|
793
|
+
|
|
794
|
+
D3
|
|
795
|
+
\`\`\`
|
|
796
|
+
|
|
797
|
+
\`\`\`single line code\`\`\`
|
|
798
|
+
|
|
799
|
+
E1E2
|
|
800
|
+
|
|
801
|
+
E3
|
|
802
|
+
`);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
it('tables', () => {
|
|
806
|
+
const markdown = `
|
|
807
|
+
| a | b |
|
|
808
|
+
| --- | --- |
|
|
809
|
+
| c | d |
|
|
810
|
+
`;
|
|
811
|
+
expect(normalizeMarkdown(markdown, true)).toBe(markdown);
|
|
812
|
+
});
|
|
813
|
+
});
|
|
814
|
+
|
|
815
|
+
describe('normalizeMarkdown - shouldMergeAdjacentLines = false', () => {
|
|
816
|
+
it('should not combine lines separated by a single \n', () => {
|
|
817
|
+
const markdown = `
|
|
818
|
+
A1
|
|
819
|
+
A2
|
|
820
|
+
|
|
821
|
+
A3
|
|
822
|
+
|
|
823
|
+
\`\`\`md
|
|
824
|
+
B1
|
|
825
|
+
B2
|
|
826
|
+
|
|
827
|
+
B3
|
|
828
|
+
\`\`\`
|
|
829
|
+
|
|
830
|
+
C1
|
|
831
|
+
C2
|
|
832
|
+
|
|
833
|
+
C3
|
|
834
|
+
|
|
835
|
+
\`\`\`js
|
|
836
|
+
D1
|
|
837
|
+
D2
|
|
838
|
+
|
|
839
|
+
D3
|
|
840
|
+
\`\`\`
|
|
841
|
+
|
|
842
|
+
\`\`\`single line code\`\`\`
|
|
843
|
+
|
|
844
|
+
E1
|
|
845
|
+
E2
|
|
846
|
+
|
|
847
|
+
E3
|
|
848
|
+
`;
|
|
849
|
+
expect(normalizeMarkdown(markdown, false)).toBe(`
|
|
850
|
+
A1
|
|
851
|
+
A2
|
|
852
|
+
|
|
853
|
+
A3
|
|
854
|
+
|
|
855
|
+
\`\`\`md
|
|
856
|
+
B1
|
|
857
|
+
B2
|
|
858
|
+
|
|
859
|
+
B3
|
|
860
|
+
\`\`\`
|
|
861
|
+
|
|
862
|
+
C1
|
|
863
|
+
C2
|
|
864
|
+
|
|
865
|
+
C3
|
|
866
|
+
|
|
867
|
+
\`\`\`js
|
|
868
|
+
D1
|
|
869
|
+
D2
|
|
870
|
+
|
|
871
|
+
D3
|
|
872
|
+
\`\`\`
|
|
873
|
+
|
|
874
|
+
\`\`\`single line code\`\`\`
|
|
875
|
+
|
|
876
|
+
E1
|
|
877
|
+
E2
|
|
878
|
+
|
|
879
|
+
E3
|
|
880
|
+
`);
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
it('tables', () => {
|
|
884
|
+
const markdown = `
|
|
885
|
+
| a | b |
|
|
886
|
+
| --- | --- |
|
|
887
|
+
| c | d |
|
|
888
|
+
`;
|
|
889
|
+
expect(normalizeMarkdown(markdown, false)).toBe(markdown);
|
|
890
|
+
});
|
|
891
|
+
});
|