@khanacademy/simple-markdown 0.8.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.
@@ -0,0 +1,4946 @@
1
+ /* eslint-disable no-var, no-unused-vars, no-console, import/no-commonjs, no-redeclare, no-useless-escape */
2
+ // @flow
3
+ /* @ts-check */
4
+ import {render} from "@testing-library/react";
5
+
6
+ // As of 2019-11-03, flow doesn't have definitions for assert.strict:
7
+ // https://github.com/facebook/flow/pull/7660
8
+ // So we use a /*::*/ hack to satisfy flow:
9
+ import SimpleMarkdown from "../index.js";
10
+
11
+ var assert = require("assert") /*:: || {} */.strict;
12
+
13
+ var React = require("react");
14
+ var ReactDOMServer = require("react-dom/server");
15
+
16
+ var inlineParse = SimpleMarkdown.defaultInlineParse;
17
+ var blockParse = SimpleMarkdown.defaultBlockParse;
18
+ var implicitParse = SimpleMarkdown.defaultImplicitParse;
19
+ var defaultReactOutput = SimpleMarkdown.defaultReactOutput;
20
+ var defaultHtmlOutput = SimpleMarkdown.defaultHtmlOutput;
21
+
22
+ /*:: // Flow definitions & hackery
23
+
24
+ var FLOW_IGNORE_COVARIANCE = {
25
+ console: {
26
+ warn: (console.warn : any),
27
+ },
28
+ };
29
+ */
30
+
31
+ /**
32
+ * A pretty-printer that handles `undefined` and functions better
33
+ * than JSON.stringify
34
+ * Important because some AST node fields can be undefined, and
35
+ * if those don't show up in the assert output, it can be
36
+ * very confusing to figure out how the actual and expected differ
37
+ * Whether node's util.inspect or JSON.stringify is better seems
38
+ * context dependent.
39
+ *
40
+ * @param {SimpleMarkdown.ASTNode | Array<SimpleMarkdown.TableAlignment>} ast
41
+ */
42
+ var prettyPrintAST = function (ast) {
43
+ return JSON.stringify(ast, null, 4);
44
+ // // FIXME(aria): For debugging in more depth? This used to work?
45
+ // return nodeUtil.inspect(ast, {
46
+ // depth: null,
47
+ // colors: false
48
+ // });
49
+ };
50
+
51
+ /**
52
+ * Asset that two ast parse trees are equal
53
+ * @param {SimpleMarkdown.ASTNode | Array<SimpleMarkdown.TableAlignment>} parsed
54
+ * @param {SimpleMarkdown.ASTNode | Array<SimpleMarkdown.TableAlignment>} expected
55
+ */
56
+ var validateParse = function (parsed, expected) {
57
+ assert.deepEqual(parsed, expected);
58
+ };
59
+
60
+ /**
61
+ * @param {SimpleMarkdown.ReactElements} reactElements
62
+ * @returns {string}
63
+ */
64
+ var reactToHtml = function (reactElements) {
65
+ var rawHtml = ReactDOMServer.renderToStaticMarkup(
66
+ React.createElement("div", {}, reactElements),
67
+ );
68
+ var innerHtml = rawHtml.replace(/^<div>/, "").replace(/<\/div>$/, "");
69
+ var simplifiedHtml = innerHtml
70
+ .replace(/>\n*/g, ">")
71
+ .replace(/\n*</g, "<")
72
+ .replace(/\s+/g, " ");
73
+ return simplifiedHtml;
74
+ };
75
+
76
+ /**
77
+ * @param {SimpleMarkdown.ASTNode} parsed
78
+ * @returns {string}
79
+ */
80
+ var htmlThroughReact = function (parsed) {
81
+ var output = defaultReactOutput(parsed);
82
+ return reactToHtml(output);
83
+ };
84
+
85
+ /**
86
+ * @param {string} source
87
+ * @returns {string}
88
+ */
89
+ var htmlFromReactMarkdown = function (source) {
90
+ return htmlThroughReact(implicitParse(source));
91
+ };
92
+
93
+ /**
94
+ * @param {string} source
95
+ * @returns {string}
96
+ */
97
+ var htmlFromMarkdown = function (source) {
98
+ var html = defaultHtmlOutput(implicitParse(source));
99
+ var simplifiedHtml = html.replace(/\s+/g, " ");
100
+ return simplifiedHtml;
101
+ };
102
+
103
+ /**
104
+ * @param {string} source
105
+ * @param {string} html
106
+ */
107
+ var assertParsesToReact = function (source, html) {
108
+ var actualHtml = htmlFromReactMarkdown(source);
109
+ assert.strictEqual(actualHtml, html);
110
+ };
111
+
112
+ /**
113
+ * @param {string} source
114
+ * @param {string} html
115
+ */
116
+ var assertParsesToHtml = function (source, html) {
117
+ var actualHtml = htmlFromMarkdown(source);
118
+ assert.strictEqual(actualHtml, html);
119
+ };
120
+
121
+ describe("simple markdown", function () {
122
+ describe("parser", function () {
123
+ it("should parse a plain string", function () {
124
+ var parsed = inlineParse("hi there");
125
+ validateParse(parsed, [
126
+ {
127
+ type: "text",
128
+ content: "hi there",
129
+ },
130
+ ]);
131
+ });
132
+
133
+ it("should parse bold", function () {
134
+ var parsed = inlineParse("**hi**");
135
+ validateParse(parsed, [
136
+ {
137
+ type: "strong",
138
+ content: [
139
+ {
140
+ type: "text",
141
+ content: "hi",
142
+ },
143
+ ],
144
+ },
145
+ ]);
146
+ });
147
+
148
+ it("should parse italics", function () {
149
+ var parsed = inlineParse("*hi*");
150
+ validateParse(parsed, [
151
+ {
152
+ type: "em",
153
+ content: [
154
+ {
155
+ type: "text",
156
+ content: "hi",
157
+ },
158
+ ],
159
+ },
160
+ ]);
161
+
162
+ var parsed2 = inlineParse("*test i*");
163
+ validateParse(parsed2, [
164
+ {
165
+ type: "em",
166
+ content: [
167
+ {
168
+ type: "text",
169
+ content: "test i",
170
+ },
171
+ ],
172
+ },
173
+ ]);
174
+ });
175
+
176
+ it("should not parse ** as empty italics", function () {
177
+ var parsed = inlineParse("**");
178
+ validateParse(parsed, [
179
+ {type: "text", content: "*"},
180
+ {type: "text", content: "*"},
181
+ ]);
182
+ });
183
+
184
+ it("should parse a single italic character", function () {
185
+ var parsed = inlineParse("*h*");
186
+ validateParse(parsed, [
187
+ {
188
+ type: "em",
189
+ content: [
190
+ {
191
+ type: "text",
192
+ content: "h",
193
+ },
194
+ ],
195
+ },
196
+ ]);
197
+ });
198
+
199
+ it("should parse strikethrough", function () {
200
+ var parsed = inlineParse("~~hi~~");
201
+ validateParse(parsed, [
202
+ {
203
+ type: "del",
204
+ content: [
205
+ {
206
+ type: "text",
207
+ content: "hi",
208
+ },
209
+ ],
210
+ },
211
+ ]);
212
+
213
+ // not super important that it parses this like this, but
214
+ // it should be a valid something...
215
+ var parsed2 = inlineParse("~~~~~");
216
+ validateParse(parsed2, [
217
+ {content: "~", type: "text"},
218
+ {content: "~", type: "text"},
219
+ {content: "~", type: "text"},
220
+ {content: "~", type: "text"},
221
+ {content: "~", type: "text"},
222
+ ]);
223
+ });
224
+
225
+ it("should support escapes in strikethrough", function () {
226
+ validateParse(inlineParse("~~hi\\~~ there~~"), [
227
+ {
228
+ type: "del",
229
+ content: [
230
+ {type: "text", content: "hi"},
231
+ {type: "text", content: "~"},
232
+ {type: "text", content: "~ there"},
233
+ ],
234
+ },
235
+ ]);
236
+ });
237
+
238
+ it("should not allow strikethrough to contain non-closing ~~s", function () {
239
+ validateParse(inlineParse("~~hi ~~there~~"), [
240
+ {type: "text", content: "~"},
241
+ {type: "text", content: "~hi "},
242
+ {type: "del", content: [{type: "text", content: "there"}]},
243
+ ]);
244
+ });
245
+
246
+ it("should parse underlines", function () {
247
+ var parsed = inlineParse("__hi__");
248
+ validateParse(parsed, [
249
+ {
250
+ type: "u",
251
+ content: [
252
+ {
253
+ type: "text",
254
+ content: "hi",
255
+ },
256
+ ],
257
+ },
258
+ ]);
259
+ });
260
+
261
+ it("should parse nested bold/italics", function () {
262
+ var parsed = inlineParse("***hi***");
263
+ validateParse(parsed, [
264
+ {
265
+ type: "em",
266
+ content: [
267
+ {
268
+ type: "strong",
269
+ content: [
270
+ {
271
+ type: "text",
272
+ content: "hi",
273
+ },
274
+ ],
275
+ },
276
+ ],
277
+ },
278
+ ]);
279
+ });
280
+
281
+ it("should parse nested bold/italics/underline", function () {
282
+ var parsed1 = inlineParse("***__hi__***");
283
+ validateParse(parsed1, [
284
+ {
285
+ type: "em",
286
+ content: [
287
+ {
288
+ type: "strong",
289
+ content: [
290
+ {
291
+ type: "u",
292
+ content: [
293
+ {
294
+ type: "text",
295
+ content: "hi",
296
+ },
297
+ ],
298
+ },
299
+ ],
300
+ },
301
+ ],
302
+ },
303
+ ]);
304
+
305
+ var parsed2 = inlineParse("*__**hi**__*");
306
+ validateParse(parsed2, [
307
+ {
308
+ type: "em",
309
+ content: [
310
+ {
311
+ type: "u",
312
+ content: [
313
+ {
314
+ type: "strong",
315
+ content: [
316
+ {
317
+ type: "text",
318
+ content: "hi",
319
+ },
320
+ ],
321
+ },
322
+ ],
323
+ },
324
+ ],
325
+ },
326
+ ]);
327
+
328
+ var parsed3 = inlineParse("***bolditalics***");
329
+ validateParse(parsed3, [
330
+ {
331
+ type: "em",
332
+ content: [
333
+ {
334
+ type: "strong",
335
+ content: [
336
+ {
337
+ type: "text",
338
+ content: "bolditalics",
339
+ },
340
+ ],
341
+ },
342
+ ],
343
+ },
344
+ ]);
345
+
346
+ var parsed4 = inlineParse("**bold *italics***");
347
+ validateParse(parsed4, [
348
+ {
349
+ type: "strong",
350
+ content: [
351
+ {
352
+ type: "text",
353
+ content: "bold ",
354
+ },
355
+ {
356
+ type: "em",
357
+ content: [
358
+ {
359
+ type: "text",
360
+ content: "italics",
361
+ },
362
+ ],
363
+ },
364
+ ],
365
+ },
366
+ ]);
367
+ });
368
+
369
+ it("should allow escaped underscores in underscore italics", function () {
370
+ var parsed1 = inlineParse("_ABC\\_DEF_");
371
+ validateParse(parsed1, [
372
+ {
373
+ type: "em",
374
+ content: [
375
+ {
376
+ type: "text",
377
+ content: "ABC",
378
+ },
379
+ {
380
+ type: "text",
381
+ content: "_",
382
+ },
383
+ {
384
+ type: "text",
385
+ content: "DEF",
386
+ },
387
+ ],
388
+ },
389
+ ]);
390
+
391
+ var parsed2 = inlineParse("_**ABC\\_DEF**_");
392
+ validateParse(parsed2, [
393
+ {
394
+ type: "em",
395
+ content: [
396
+ {
397
+ type: "strong",
398
+ content: [
399
+ {
400
+ type: "text",
401
+ content: "ABC",
402
+ },
403
+ {
404
+ type: "text",
405
+ content: "_",
406
+ },
407
+ {
408
+ type: "text",
409
+ content: "DEF",
410
+ },
411
+ ],
412
+ },
413
+ ],
414
+ },
415
+ ]);
416
+
417
+ var parsed3 = inlineParse("_**ABC\\$DEF**_");
418
+ validateParse(parsed3, [
419
+ {
420
+ type: "em",
421
+ content: [
422
+ {
423
+ type: "strong",
424
+ content: [
425
+ {
426
+ type: "text",
427
+ content: "ABC",
428
+ },
429
+ {
430
+ type: "text",
431
+ content: "$",
432
+ },
433
+ {
434
+ type: "text",
435
+ content: "DEF",
436
+ },
437
+ ],
438
+ },
439
+ ],
440
+ },
441
+ ]);
442
+
443
+ validateParse(inlineParse("_\\\\_"), [
444
+ {
445
+ type: "em",
446
+ content: [
447
+ {
448
+ type: "text",
449
+ content: "\\",
450
+ },
451
+ ],
452
+ },
453
+ ]);
454
+ });
455
+
456
+ it("should allow escaped asterisks in asterisk italics", function () {
457
+ var parsed1 = inlineParse("*hi\\* there*");
458
+ validateParse(parsed1, [
459
+ {
460
+ type: "em",
461
+ content: [
462
+ {
463
+ type: "text",
464
+ content: "hi",
465
+ },
466
+ {
467
+ type: "text",
468
+ content: "*",
469
+ },
470
+ {
471
+ type: "text",
472
+ content: " there",
473
+ },
474
+ ],
475
+ },
476
+ ]);
477
+
478
+ var parsed2 = inlineParse("_**ABC\\*DEF**_");
479
+ validateParse(parsed2, [
480
+ {
481
+ type: "em",
482
+ content: [
483
+ {
484
+ type: "strong",
485
+ content: [
486
+ {
487
+ type: "text",
488
+ content: "ABC",
489
+ },
490
+ {
491
+ type: "text",
492
+ content: "*",
493
+ },
494
+ {
495
+ type: "text",
496
+ content: "DEF",
497
+ },
498
+ ],
499
+ },
500
+ ],
501
+ },
502
+ ]);
503
+ });
504
+
505
+ it("should allow escaped asterisks in asterisk bolds", function () {
506
+ var parsed1 = inlineParse("**hi\\* there**");
507
+ validateParse(parsed1, [
508
+ {
509
+ type: "strong",
510
+ content: [
511
+ {
512
+ type: "text",
513
+ content: "hi",
514
+ },
515
+ {
516
+ type: "text",
517
+ content: "*",
518
+ },
519
+ {
520
+ type: "text",
521
+ content: " there",
522
+ },
523
+ ],
524
+ },
525
+ ]);
526
+
527
+ validateParse(inlineParse("**hi\\** there**"), [
528
+ {
529
+ type: "strong",
530
+ content: [
531
+ {
532
+ type: "text",
533
+ content: "hi",
534
+ },
535
+ {
536
+ type: "text",
537
+ content: "*",
538
+ },
539
+ {
540
+ type: "text",
541
+ content: "* there",
542
+ },
543
+ ],
544
+ },
545
+ ]);
546
+ });
547
+
548
+ it("should allow escaped underscores in underlines", function () {
549
+ var parsed1 = inlineParse("__hi\\__ there__");
550
+ validateParse(parsed1, [
551
+ {
552
+ type: "u",
553
+ content: [
554
+ {
555
+ type: "text",
556
+ content: "hi",
557
+ },
558
+ {
559
+ type: "text",
560
+ content: "_",
561
+ },
562
+ {
563
+ type: "text",
564
+ content: "_ there",
565
+ },
566
+ ],
567
+ },
568
+ ]);
569
+ });
570
+
571
+ it("should parse complex combined bold/italics", function () {
572
+ var parsed = inlineParse("***bold** italics*");
573
+ validateParse(parsed, [
574
+ {
575
+ type: "em",
576
+ content: [
577
+ {
578
+ type: "strong",
579
+ content: [
580
+ {
581
+ type: "text",
582
+ content: "bold",
583
+ },
584
+ ],
585
+ },
586
+ {
587
+ type: "text",
588
+ content: " italics",
589
+ },
590
+ ],
591
+ },
592
+ ]);
593
+
594
+ var parsed2 = inlineParse("*hi **there you***");
595
+ validateParse(parsed2, [
596
+ {
597
+ type: "em",
598
+ content: [
599
+ {
600
+ type: "text",
601
+ content: "hi ",
602
+ },
603
+ {
604
+ type: "strong",
605
+ content: [
606
+ {
607
+ type: "text",
608
+ content: "there you",
609
+ },
610
+ ],
611
+ },
612
+ ],
613
+ },
614
+ ]);
615
+
616
+ var parsed3 = inlineParse("***like* this**");
617
+ validateParse(parsed3, [
618
+ {
619
+ type: "strong",
620
+ content: [
621
+ {
622
+ type: "em",
623
+ content: [
624
+ {
625
+ type: "text",
626
+ content: "like",
627
+ },
628
+ ],
629
+ },
630
+ {
631
+ type: "text",
632
+ content: " this",
633
+ },
634
+ ],
635
+ },
636
+ ]);
637
+
638
+ var parsed4 = inlineParse("**bold *and italics***");
639
+ validateParse(parsed4, [
640
+ {
641
+ type: "strong",
642
+ content: [
643
+ {
644
+ type: "text",
645
+ content: "bold ",
646
+ },
647
+ {
648
+ type: "em",
649
+ content: [
650
+ {
651
+ type: "text",
652
+ content: "and italics",
653
+ },
654
+ ],
655
+ },
656
+ ],
657
+ },
658
+ ]);
659
+ });
660
+
661
+ it("should parse multiple bold/italics/underlines", function () {
662
+ var parsed = inlineParse("*some* of this __sentence__ is **bold**");
663
+ validateParse(parsed, [
664
+ {
665
+ type: "em",
666
+ content: [
667
+ {
668
+ type: "text",
669
+ content: "some",
670
+ },
671
+ ],
672
+ },
673
+ {
674
+ type: "text",
675
+ content: " of this ",
676
+ },
677
+ {
678
+ type: "u",
679
+ content: [
680
+ {
681
+ type: "text",
682
+ content: "sentence",
683
+ },
684
+ ],
685
+ },
686
+ {
687
+ type: "text",
688
+ content: " is ",
689
+ },
690
+ {
691
+ type: "strong",
692
+ content: [
693
+ {
694
+ type: "text",
695
+ content: "bold",
696
+ },
697
+ ],
698
+ },
699
+ ]);
700
+
701
+ validateParse(inlineParse("_italics __bold___"), [
702
+ {
703
+ type: "em",
704
+ content: [
705
+ {
706
+ type: "text",
707
+ content: "italics ",
708
+ },
709
+ {
710
+ type: "u",
711
+ content: [
712
+ {
713
+ type: "text",
714
+ content: "bold",
715
+ },
716
+ ],
717
+ },
718
+ ],
719
+ },
720
+ ]);
721
+ });
722
+
723
+ it("should parse inline code", function () {
724
+ var parsed = inlineParse("`hi`");
725
+ validateParse(parsed, [
726
+ {
727
+ type: "inlineCode",
728
+ content: "hi",
729
+ },
730
+ ]);
731
+ });
732
+
733
+ it("should parse * and _ inside `` as code", function () {
734
+ var parsed = inlineParse("`const int * const * const p; // _hi_`");
735
+ validateParse(parsed, [
736
+ {
737
+ type: "inlineCode",
738
+ content: "const int * const * const p; // _hi_",
739
+ },
740
+ ]);
741
+ });
742
+
743
+ it("should ignore a single space at the start and end of an inline code block separating a '`'", function () {
744
+ var parsed1 = inlineParse("test `` ` `` escaping a code block");
745
+ validateParse(parsed1, [
746
+ {type: "text", content: "test "},
747
+ {type: "inlineCode", content: "`"},
748
+ {type: "text", content: " escaping a code block"},
749
+ ]);
750
+
751
+ var parsed1 = inlineParse("test `` ` `` escaping a code block");
752
+ validateParse(parsed1, [
753
+ {type: "text", content: "test "},
754
+ {type: "inlineCode", content: " ` "},
755
+ {type: "text", content: " escaping a code block"},
756
+ ]);
757
+ });
758
+
759
+ it("should allow you to escape special characters with \\", function () {
760
+ var parsed = inlineParse("\\`hi\\` \\*bye\\* \\~\\|\\<\\[\\{");
761
+ validateParse(parsed, [
762
+ {type: "text", content: "`"},
763
+ {type: "text", content: "hi"},
764
+ {type: "text", content: "`"},
765
+ {type: "text", content: " "},
766
+ {type: "text", content: "*"},
767
+ {type: "text", content: "bye"},
768
+ {type: "text", content: "*"},
769
+ {type: "text", content: " "},
770
+ {type: "text", content: "~"},
771
+ {type: "text", content: "|"},
772
+ {type: "text", content: "<"},
773
+ {type: "text", content: "["},
774
+ {type: "text", content: "{"},
775
+ ]);
776
+
777
+ var parsed2 = inlineParse("hi\\^caret");
778
+ validateParse(parsed2, [
779
+ {type: "text", content: "hi"},
780
+ {type: "text", content: "^"},
781
+ {type: "text", content: "caret"},
782
+ ]);
783
+ });
784
+
785
+ it("should parse basic []() links as links", function () {
786
+ var parsed = inlineParse("[hi](http://www.google.com)");
787
+ validateParse(parsed, [
788
+ {
789
+ type: "link",
790
+ content: [
791
+ {
792
+ type: "text",
793
+ content: "hi",
794
+ },
795
+ ],
796
+ target: "http://www.google.com",
797
+ title: undefined,
798
+ },
799
+ ]);
800
+
801
+ var parsed2 = inlineParse("[secure](https://www.google.com)");
802
+ validateParse(parsed2, [
803
+ {
804
+ type: "link",
805
+ content: [
806
+ {
807
+ type: "text",
808
+ content: "secure",
809
+ },
810
+ ],
811
+ target: "https://www.google.com",
812
+ title: undefined,
813
+ },
814
+ ]);
815
+
816
+ var parsed3 = inlineParse(
817
+ "[local](http://localhost:9000/test.html)",
818
+ );
819
+ validateParse(parsed3, [
820
+ {
821
+ type: "link",
822
+ content: [
823
+ {
824
+ type: "text",
825
+ content: "local",
826
+ },
827
+ ],
828
+ target: "http://localhost:9000/test.html",
829
+ title: undefined,
830
+ },
831
+ ]);
832
+
833
+ var parsed4 = inlineParse(
834
+ "[params](http://localhost:9000/test.html" +
835
+ "?content=%7B%7D&format=pretty)",
836
+ );
837
+ validateParse(parsed4, [
838
+ {
839
+ type: "link",
840
+ content: [
841
+ {
842
+ type: "text",
843
+ content: "params",
844
+ },
845
+ ],
846
+ target:
847
+ "http://localhost:9000/test.html" +
848
+ "?content=%7B%7D&format=pretty",
849
+ title: undefined,
850
+ },
851
+ ]);
852
+
853
+ var parsed5 = inlineParse(
854
+ "[hash](http://localhost:9000/test.html#content=%7B%7D)",
855
+ );
856
+ validateParse(parsed5, [
857
+ {
858
+ type: "link",
859
+ content: [
860
+ {
861
+ type: "text",
862
+ content: "hash",
863
+ },
864
+ ],
865
+ target: "http://localhost:9000/test.html#content=%7B%7D",
866
+ title: undefined,
867
+ },
868
+ ]);
869
+ });
870
+
871
+ it("should allow escaping `[` with `\\`", function () {
872
+ // Without the backslash, the following would be a
873
+ // link with the text "hi".
874
+ // With the backslash, it should ignore the '[hi]'
875
+ // portion, but will still detect that the inside
876
+ // of the parentheses contains a raw url, which it
877
+ // will turn into a url link.
878
+ var parsed = inlineParse("\\[hi](http://www.google.com)");
879
+ validateParse(parsed, [
880
+ {content: "[", type: "text"},
881
+ {content: "hi", type: "text"},
882
+ {content: "]", type: "text"},
883
+ {content: "(", type: "text"},
884
+ {
885
+ type: "link",
886
+ content: [
887
+ {
888
+ type: "text",
889
+ content: "http://www.google.com",
890
+ },
891
+ ],
892
+ target: "http://www.google.com",
893
+ title: undefined,
894
+ },
895
+ {content: ")", type: "text"},
896
+ ]);
897
+ });
898
+
899
+ it("should allow escaping of link urls with `\\`", function () {
900
+ var parsed = inlineParse(
901
+ "[test link](https://test.link/\\(test\\))",
902
+ );
903
+ validateParse(parsed, [
904
+ {
905
+ type: "link",
906
+ content: [
907
+ {
908
+ type: "text",
909
+ content: "test link",
910
+ },
911
+ ],
912
+ target: "https://test.link/(test)",
913
+ title: undefined,
914
+ },
915
+ ]);
916
+ });
917
+
918
+ it("should allow one level of balanced parens in link urls", function () {
919
+ var parsed = inlineParse("[link]((foo)and(bar))");
920
+ validateParse(parsed, [
921
+ {
922
+ type: "link",
923
+ content: [
924
+ {
925
+ content: "link",
926
+ type: "text",
927
+ },
928
+ ],
929
+ target: "(foo)and(bar)",
930
+ title: undefined,
931
+ },
932
+ ]);
933
+ });
934
+
935
+ it("should parse basic <autolinks>", function () {
936
+ var parsed = inlineParse("<http://www.google.com>");
937
+ validateParse(parsed, [
938
+ {
939
+ type: "link",
940
+ content: [
941
+ {
942
+ type: "text",
943
+ content: "http://www.google.com",
944
+ },
945
+ ],
946
+ target: "http://www.google.com",
947
+ },
948
+ ]);
949
+
950
+ var parsed2 = inlineParse("<https://www.google.com>");
951
+ validateParse(parsed2, [
952
+ {
953
+ type: "link",
954
+ content: [
955
+ {
956
+ type: "text",
957
+ content: "https://www.google.com",
958
+ },
959
+ ],
960
+ target: "https://www.google.com",
961
+ },
962
+ ]);
963
+
964
+ var parsed3 = inlineParse("<http://localhost:9000/test.html>");
965
+ validateParse(parsed3, [
966
+ {
967
+ type: "link",
968
+ content: [
969
+ {
970
+ type: "text",
971
+ content: "http://localhost:9000/test.html",
972
+ },
973
+ ],
974
+ target: "http://localhost:9000/test.html",
975
+ },
976
+ ]);
977
+
978
+ var parsed4 = inlineParse(
979
+ "<http://localhost:9000/test.html" +
980
+ "?content=%7B%7D&format=pretty>",
981
+ );
982
+ validateParse(parsed4, [
983
+ {
984
+ type: "link",
985
+ content: [
986
+ {
987
+ type: "text",
988
+ content:
989
+ "http://localhost:9000/test.html" +
990
+ "?content=%7B%7D&format=pretty",
991
+ },
992
+ ],
993
+ target:
994
+ "http://localhost:9000/test.html" +
995
+ "?content=%7B%7D&format=pretty",
996
+ },
997
+ ]);
998
+
999
+ var parsed5 = inlineParse(
1000
+ "<http://localhost:9000/test.html#content=%7B%7D>",
1001
+ );
1002
+ validateParse(parsed5, [
1003
+ {
1004
+ type: "link",
1005
+ content: [
1006
+ {
1007
+ type: "text",
1008
+ content:
1009
+ "http://localhost:9000/test.html#content=%7B%7D",
1010
+ },
1011
+ ],
1012
+ target: "http://localhost:9000/test.html#content=%7B%7D",
1013
+ },
1014
+ ]);
1015
+ });
1016
+
1017
+ it("should parse basic <mailto@autolinks>", function () {
1018
+ var parsed = inlineParse("<test@example.com>");
1019
+ validateParse(parsed, [
1020
+ {
1021
+ type: "link",
1022
+ content: [
1023
+ {
1024
+ type: "text",
1025
+ content: "test@example.com",
1026
+ },
1027
+ ],
1028
+ target: "mailto:test@example.com",
1029
+ },
1030
+ ]);
1031
+
1032
+ var parsed2 = inlineParse("<test+ext@example.com>");
1033
+ validateParse(parsed2, [
1034
+ {
1035
+ type: "link",
1036
+ content: [
1037
+ {
1038
+ type: "text",
1039
+ content: "test+ext@example.com",
1040
+ },
1041
+ ],
1042
+ target: "mailto:test+ext@example.com",
1043
+ },
1044
+ ]);
1045
+
1046
+ var parsed3 = inlineParse("<mailto:test@example.com>");
1047
+ validateParse(parsed3, [
1048
+ {
1049
+ type: "link",
1050
+ content: [
1051
+ {
1052
+ type: "text",
1053
+ content: "mailto:test@example.com",
1054
+ },
1055
+ ],
1056
+ target: "mailto:test@example.com",
1057
+ },
1058
+ ]);
1059
+
1060
+ var parsed4 = inlineParse("<MAILTO:TEST@EXAMPLE.COM>");
1061
+ validateParse(parsed4, [
1062
+ {
1063
+ type: "link",
1064
+ content: [
1065
+ {
1066
+ type: "text",
1067
+ content: "MAILTO:TEST@EXAMPLE.COM",
1068
+ },
1069
+ ],
1070
+ target: "MAILTO:TEST@EXAMPLE.COM",
1071
+ },
1072
+ ]);
1073
+ });
1074
+
1075
+ it("should parse basic freeform urls", function () {
1076
+ var parsed = inlineParse("http://www.google.com");
1077
+ validateParse(parsed, [
1078
+ {
1079
+ type: "link",
1080
+ content: [
1081
+ {
1082
+ type: "text",
1083
+ content: "http://www.google.com",
1084
+ },
1085
+ ],
1086
+ target: "http://www.google.com",
1087
+ title: undefined,
1088
+ },
1089
+ ]);
1090
+
1091
+ var parsed2 = inlineParse("https://www.google.com");
1092
+ validateParse(parsed2, [
1093
+ {
1094
+ type: "link",
1095
+ content: [
1096
+ {
1097
+ type: "text",
1098
+ content: "https://www.google.com",
1099
+ },
1100
+ ],
1101
+ target: "https://www.google.com",
1102
+ title: undefined,
1103
+ },
1104
+ ]);
1105
+
1106
+ var parsed3 = inlineParse("http://example.com/test.html");
1107
+ validateParse(parsed3, [
1108
+ {
1109
+ type: "link",
1110
+ content: [
1111
+ {
1112
+ type: "text",
1113
+ content: "http://example.com/test.html",
1114
+ },
1115
+ ],
1116
+ target: "http://example.com/test.html",
1117
+ title: undefined,
1118
+ },
1119
+ ]);
1120
+
1121
+ var parsed4 = inlineParse(
1122
+ "http://example.com/test.html" +
1123
+ "?content=%7B%7D&format=pretty",
1124
+ );
1125
+ validateParse(parsed4, [
1126
+ {
1127
+ type: "link",
1128
+ content: [
1129
+ {
1130
+ type: "text",
1131
+ content:
1132
+ "http://example.com/test.html" +
1133
+ "?content=%7B%7D&format=pretty",
1134
+ },
1135
+ ],
1136
+ target:
1137
+ "http://example.com/test.html" +
1138
+ "?content=%7B%7D&format=pretty",
1139
+ title: undefined,
1140
+ },
1141
+ ]);
1142
+
1143
+ var parsed5 = inlineParse(
1144
+ "http://example.com/test.html#content=%7B%7D",
1145
+ );
1146
+ validateParse(parsed5, [
1147
+ {
1148
+ type: "link",
1149
+ content: [
1150
+ {
1151
+ type: "text",
1152
+ content:
1153
+ "http://example.com/test.html#content=%7B%7D",
1154
+ },
1155
+ ],
1156
+ target: "http://example.com/test.html#content=%7B%7D",
1157
+ title: undefined,
1158
+ },
1159
+ ]);
1160
+ });
1161
+
1162
+ it("should not split words before colons", function () {
1163
+ var parsed = inlineParse("Here is a rule: try this");
1164
+ validateParse(parsed, [
1165
+ {
1166
+ type: "text",
1167
+ content: "Here is a rule",
1168
+ },
1169
+ {
1170
+ type: "text",
1171
+ content: ": try this",
1172
+ },
1173
+ ]);
1174
+ });
1175
+
1176
+ it("should parse freeform urls inside paragraphs", function () {
1177
+ var parsed = blockParse(
1178
+ "hi this is a link http://www.google.com\n\n",
1179
+ );
1180
+ validateParse(parsed, [
1181
+ {
1182
+ type: "paragraph",
1183
+ content: [
1184
+ {
1185
+ type: "text",
1186
+ content: "hi this is a link ",
1187
+ },
1188
+ {
1189
+ type: "link",
1190
+ content: [
1191
+ {
1192
+ type: "text",
1193
+ content: "http://www.google.com",
1194
+ },
1195
+ ],
1196
+ target: "http://www.google.com",
1197
+ title: undefined,
1198
+ },
1199
+ ],
1200
+ },
1201
+ ]);
1202
+ });
1203
+
1204
+ it("should parse [reflinks][and their targets]", function () {
1205
+ var parsed = implicitParse(
1206
+ "[Google][1]\n\n" + "[1]: http://www.google.com\n\n",
1207
+ );
1208
+ validateParse(parsed, [
1209
+ {
1210
+ type: "paragraph",
1211
+ content: [
1212
+ {
1213
+ type: "link",
1214
+ content: [
1215
+ {
1216
+ type: "text",
1217
+ content: "Google",
1218
+ },
1219
+ ],
1220
+ target: "http://www.google.com",
1221
+ title: undefined,
1222
+ },
1223
+ ],
1224
+ },
1225
+ {
1226
+ type: "def",
1227
+ def: "1",
1228
+ target: "http://www.google.com",
1229
+ title: undefined,
1230
+ },
1231
+ ]);
1232
+
1233
+ var parsed2 = blockParse(
1234
+ "[1]: http://www.google.com\n\n" + "[Google][1]\n\n",
1235
+ );
1236
+ validateParse(parsed2, [
1237
+ {
1238
+ type: "def",
1239
+ def: "1",
1240
+ target: "http://www.google.com",
1241
+ title: undefined,
1242
+ },
1243
+ {
1244
+ type: "paragraph",
1245
+ content: [
1246
+ {
1247
+ type: "link",
1248
+ content: [
1249
+ {
1250
+ type: "text",
1251
+ content: "Google",
1252
+ },
1253
+ ],
1254
+ target: "http://www.google.com",
1255
+ title: undefined,
1256
+ },
1257
+ ],
1258
+ },
1259
+ ]);
1260
+ });
1261
+
1262
+ it("should parse inline link titles", function () {
1263
+ var parsed = inlineParse(
1264
+ '[Google](http://www.google.com "This is google!")',
1265
+ );
1266
+ validateParse(parsed, [
1267
+ {
1268
+ type: "link",
1269
+ content: [
1270
+ {
1271
+ type: "text",
1272
+ content: "Google",
1273
+ },
1274
+ ],
1275
+ target: "http://www.google.com",
1276
+ title: "This is google!",
1277
+ },
1278
+ ]);
1279
+
1280
+ var parsed2 = inlineParse(
1281
+ '[Google](http://www.google.com "still Google")',
1282
+ );
1283
+ validateParse(parsed2, [
1284
+ {
1285
+ type: "link",
1286
+ content: [
1287
+ {
1288
+ type: "text",
1289
+ content: "Google",
1290
+ },
1291
+ ],
1292
+ target: "http://www.google.com",
1293
+ title: "still Google",
1294
+ },
1295
+ ]);
1296
+ });
1297
+
1298
+ it("should parse reflink titles", function () {
1299
+ var parsed = implicitParse(
1300
+ "[Google][1]\n\n" +
1301
+ "[1]: http://www.google.com (This is google!)\n\n",
1302
+ );
1303
+ validateParse(parsed, [
1304
+ {
1305
+ type: "paragraph",
1306
+ content: [
1307
+ {
1308
+ type: "link",
1309
+ content: [
1310
+ {
1311
+ type: "text",
1312
+ content: "Google",
1313
+ },
1314
+ ],
1315
+ target: "http://www.google.com",
1316
+ title: "This is google!",
1317
+ },
1318
+ ],
1319
+ },
1320
+ {
1321
+ type: "def",
1322
+ def: "1",
1323
+ target: "http://www.google.com",
1324
+ title: "This is google!",
1325
+ },
1326
+ ]);
1327
+
1328
+ var parsed2 = implicitParse(
1329
+ '[1]: http://www.google.com "still Google"\n\n' +
1330
+ "[Google][1]\n\n",
1331
+ );
1332
+ validateParse(parsed2, [
1333
+ {
1334
+ type: "def",
1335
+ def: "1",
1336
+ target: "http://www.google.com",
1337
+ title: "still Google",
1338
+ },
1339
+ {
1340
+ type: "paragraph",
1341
+ content: [
1342
+ {
1343
+ type: "link",
1344
+ content: [
1345
+ {
1346
+ type: "text",
1347
+ content: "Google",
1348
+ },
1349
+ ],
1350
+ target: "http://www.google.com",
1351
+ title: "still Google",
1352
+ },
1353
+ ],
1354
+ },
1355
+ ]);
1356
+
1357
+ // test some edge cases, notably:
1358
+ // target of ""; title using parens; def with a `-` in it
1359
+ var parsed3 = implicitParse(
1360
+ "[Nowhere][nowhere-target]\n\n" +
1361
+ "[nowhere-target]: <> (nowhere)\n\n",
1362
+ );
1363
+ validateParse(parsed3, [
1364
+ {
1365
+ type: "paragraph",
1366
+ content: [
1367
+ {
1368
+ type: "link",
1369
+ content: [
1370
+ {
1371
+ type: "text",
1372
+ content: "Nowhere",
1373
+ },
1374
+ ],
1375
+ target: "",
1376
+ title: "nowhere",
1377
+ },
1378
+ ],
1379
+ },
1380
+ {
1381
+ type: "def",
1382
+ def: "nowhere-target",
1383
+ target: "",
1384
+ title: "nowhere",
1385
+ },
1386
+ ]);
1387
+ });
1388
+
1389
+ it("should parse [reflinks][] with implicit targets", function () {
1390
+ var parsed = implicitParse(
1391
+ "[Google][]\n\n" + "[Google]: http://www.google.com\n\n",
1392
+ );
1393
+ validateParse(parsed, [
1394
+ {
1395
+ type: "paragraph",
1396
+ content: [
1397
+ {
1398
+ type: "link",
1399
+ content: [
1400
+ {
1401
+ type: "text",
1402
+ content: "Google",
1403
+ },
1404
+ ],
1405
+ target: "http://www.google.com",
1406
+ title: undefined,
1407
+ },
1408
+ ],
1409
+ },
1410
+ {
1411
+ type: "def",
1412
+ def: "google",
1413
+ target: "http://www.google.com",
1414
+ title: undefined,
1415
+ },
1416
+ ]);
1417
+
1418
+ var parsed2 = implicitParse(
1419
+ "[Google]: http://www.google.com\n\n" + "[Google][]\n\n",
1420
+ );
1421
+ validateParse(parsed2, [
1422
+ {
1423
+ type: "def",
1424
+ def: "google",
1425
+ target: "http://www.google.com",
1426
+ title: undefined,
1427
+ },
1428
+ {
1429
+ type: "paragraph",
1430
+ content: [
1431
+ {
1432
+ type: "link",
1433
+ content: [
1434
+ {
1435
+ type: "text",
1436
+ content: "Google",
1437
+ },
1438
+ ],
1439
+ target: "http://www.google.com",
1440
+ title: undefined,
1441
+ },
1442
+ ],
1443
+ },
1444
+ ]);
1445
+ });
1446
+
1447
+ it("should handle multiple [reflinks][to the same target]", function () {
1448
+ var parsed = implicitParse(
1449
+ "[Google][1] [Yahoo][1]\n\n" + "[1]: http://www.google.com\n\n",
1450
+ );
1451
+ validateParse(parsed, [
1452
+ {
1453
+ type: "paragraph",
1454
+ content: [
1455
+ {
1456
+ type: "link",
1457
+ content: [
1458
+ {
1459
+ type: "text",
1460
+ content: "Google",
1461
+ },
1462
+ ],
1463
+ target: "http://www.google.com",
1464
+ title: undefined,
1465
+ },
1466
+ {
1467
+ type: "text",
1468
+ content: " ",
1469
+ },
1470
+ {
1471
+ type: "link",
1472
+ content: [
1473
+ {
1474
+ type: "text",
1475
+ content: "Yahoo",
1476
+ },
1477
+ ],
1478
+ target: "http://www.google.com",
1479
+ title: undefined,
1480
+ },
1481
+ ],
1482
+ },
1483
+ {
1484
+ type: "def",
1485
+ def: "1",
1486
+ target: "http://www.google.com",
1487
+ title: undefined,
1488
+ },
1489
+ ]);
1490
+
1491
+ // This is sort of silly, but the last def overrides all previous
1492
+ // links. This is just a test that things are continuing to work
1493
+ // as we currently expect them to, but I seriously hope no one
1494
+ // writes markdown like this!
1495
+ var parsed2 = implicitParse(
1496
+ "[test][1]\n\n" +
1497
+ "[1]: http://google.com\n\n" +
1498
+ "[test2][1]\n\n" +
1499
+ "[1]: http://khanacademy.org\n\n",
1500
+ );
1501
+ validateParse(parsed2, [
1502
+ {
1503
+ type: "paragraph",
1504
+ content: [
1505
+ {
1506
+ type: "link",
1507
+ content: [
1508
+ {
1509
+ type: "text",
1510
+ content: "test",
1511
+ },
1512
+ ],
1513
+ target: "http://khanacademy.org",
1514
+ title: undefined,
1515
+ },
1516
+ ],
1517
+ },
1518
+ {
1519
+ type: "def",
1520
+ def: "1",
1521
+ target: "http://google.com",
1522
+ title: undefined,
1523
+ },
1524
+ {
1525
+ type: "paragraph",
1526
+ content: [
1527
+ {
1528
+ type: "link",
1529
+ content: [
1530
+ {
1531
+ type: "text",
1532
+ content: "test2",
1533
+ },
1534
+ ],
1535
+ target: "http://khanacademy.org",
1536
+ title: undefined,
1537
+ },
1538
+ ],
1539
+ },
1540
+ {
1541
+ type: "def",
1542
+ def: "1",
1543
+ target: "http://khanacademy.org",
1544
+ title: undefined,
1545
+ },
1546
+ ]);
1547
+ });
1548
+
1549
+ it("should parse basic images", function () {
1550
+ var parsed = inlineParse("![](http://example.com/test.png)");
1551
+ validateParse(parsed, [
1552
+ {
1553
+ type: "image",
1554
+ alt: "",
1555
+ target: "http://example.com/test.png",
1556
+ title: undefined,
1557
+ },
1558
+ ]);
1559
+
1560
+ var parsed2 = inlineParse("![aaalt](http://example.com/image)");
1561
+ validateParse(parsed2, [
1562
+ {
1563
+ type: "image",
1564
+ alt: "aaalt",
1565
+ target: "http://example.com/image",
1566
+ title: undefined,
1567
+ },
1568
+ ]);
1569
+
1570
+ var parsed3 = inlineParse(
1571
+ '![](http://localhost:9000/test.html "local")',
1572
+ );
1573
+ validateParse(parsed3, [
1574
+ {
1575
+ type: "image",
1576
+ alt: "",
1577
+ target: "http://localhost:9000/test.html",
1578
+ title: "local",
1579
+ },
1580
+ ]);
1581
+
1582
+ var parsed4 = inlineParse(
1583
+ "![p](http://localhost:9000/test" +
1584
+ '?content=%7B%7D&format=pretty "params")',
1585
+ );
1586
+ validateParse(parsed4, [
1587
+ {
1588
+ type: "image",
1589
+ alt: "p",
1590
+ target:
1591
+ "http://localhost:9000/test" +
1592
+ "?content=%7B%7D&format=pretty",
1593
+ title: "params",
1594
+ },
1595
+ ]);
1596
+
1597
+ var parsed5 = inlineParse(
1598
+ "![hash](http://localhost:9000/test.png#content=%7B%7D)",
1599
+ );
1600
+ validateParse(parsed5, [
1601
+ {
1602
+ type: "image",
1603
+ alt: "hash",
1604
+ target: "http://localhost:9000/test.png#content=%7B%7D",
1605
+ title: undefined,
1606
+ },
1607
+ ]);
1608
+ });
1609
+
1610
+ it("should parse [refimages][and their targets]", function () {
1611
+ var parsed = implicitParse(
1612
+ "![aaalt][1]\n\n" + "[1]: http://example.com/test.gif\n\n",
1613
+ );
1614
+ validateParse(parsed, [
1615
+ {
1616
+ type: "paragraph",
1617
+ content: [
1618
+ {
1619
+ type: "image",
1620
+ alt: "aaalt",
1621
+ target: "http://example.com/test.gif",
1622
+ title: undefined,
1623
+ },
1624
+ ],
1625
+ },
1626
+ {
1627
+ type: "def",
1628
+ def: "1",
1629
+ target: "http://example.com/test.gif",
1630
+ title: undefined,
1631
+ },
1632
+ ]);
1633
+
1634
+ var parsed2 = implicitParse(
1635
+ "[image]: http://example.com/test.gif\n\n" + "![image][]\n\n",
1636
+ );
1637
+ validateParse(parsed2, [
1638
+ {
1639
+ type: "def",
1640
+ def: "image",
1641
+ target: "http://example.com/test.gif",
1642
+ title: undefined,
1643
+ },
1644
+ {
1645
+ type: "paragraph",
1646
+ content: [
1647
+ {
1648
+ type: "image",
1649
+ alt: "image",
1650
+ target: "http://example.com/test.gif",
1651
+ title: undefined,
1652
+ },
1653
+ ],
1654
+ },
1655
+ ]);
1656
+
1657
+ var parsed3 = implicitParse(
1658
+ '[image]: http://example.com/test.gif "title!"\n\n' +
1659
+ "![image][]\n\n",
1660
+ );
1661
+ validateParse(parsed3, [
1662
+ {
1663
+ type: "def",
1664
+ def: "image",
1665
+ target: "http://example.com/test.gif",
1666
+ title: "title!",
1667
+ },
1668
+ {
1669
+ type: "paragraph",
1670
+ content: [
1671
+ {
1672
+ type: "image",
1673
+ alt: "image",
1674
+ target: "http://example.com/test.gif",
1675
+ title: "title!",
1676
+ },
1677
+ ],
1678
+ },
1679
+ ]);
1680
+
1681
+ var parsed3 = implicitParse(
1682
+ "[image]: http://example.com/test.gif (*title text*)\n\n" +
1683
+ "![image][]\n\n",
1684
+ );
1685
+ validateParse(parsed3, [
1686
+ {
1687
+ type: "def",
1688
+ def: "image",
1689
+ target: "http://example.com/test.gif",
1690
+ title: "*title text*",
1691
+ },
1692
+ {
1693
+ type: "paragraph",
1694
+ content: [
1695
+ {
1696
+ type: "image",
1697
+ alt: "image",
1698
+ target: "http://example.com/test.gif",
1699
+ title: "*title text*",
1700
+ },
1701
+ ],
1702
+ },
1703
+ ]);
1704
+ });
1705
+
1706
+ it("should compare defs case- and whitespace-insensitively", function () {
1707
+ var parsed = implicitParse(
1708
+ "[Google][HiIiI]\n\n" + "[HIiii]: http://www.google.com\n\n",
1709
+ );
1710
+ validateParse(parsed, [
1711
+ {
1712
+ type: "paragraph",
1713
+ content: [
1714
+ {
1715
+ type: "link",
1716
+ content: [
1717
+ {
1718
+ type: "text",
1719
+ content: "Google",
1720
+ },
1721
+ ],
1722
+ target: "http://www.google.com",
1723
+ title: undefined,
1724
+ },
1725
+ ],
1726
+ },
1727
+ {
1728
+ type: "def",
1729
+ def: "hiiii",
1730
+ target: "http://www.google.com",
1731
+ title: undefined,
1732
+ },
1733
+ ]);
1734
+
1735
+ var parsed2 = implicitParse(
1736
+ "[Google][]\n\n" + "[google]: http://www.google.com\n\n",
1737
+ );
1738
+ validateParse(parsed2, [
1739
+ {
1740
+ type: "paragraph",
1741
+ content: [
1742
+ {
1743
+ type: "link",
1744
+ content: [
1745
+ {
1746
+ type: "text",
1747
+ content: "Google",
1748
+ },
1749
+ ],
1750
+ target: "http://www.google.com",
1751
+ title: undefined,
1752
+ },
1753
+ ],
1754
+ },
1755
+ {
1756
+ type: "def",
1757
+ def: "google",
1758
+ target: "http://www.google.com",
1759
+ title: undefined,
1760
+ },
1761
+ ]);
1762
+
1763
+ var parsed3 = implicitParse(
1764
+ "[Google][ h i ]\n\n" +
1765
+ "[ h i ]: http://www.google.com\n\n",
1766
+ );
1767
+ validateParse(parsed3, [
1768
+ {
1769
+ type: "paragraph",
1770
+ content: [
1771
+ {
1772
+ type: "link",
1773
+ content: [
1774
+ {
1775
+ type: "text",
1776
+ content: "Google",
1777
+ },
1778
+ ],
1779
+ target: "http://www.google.com",
1780
+ title: undefined,
1781
+ },
1782
+ ],
1783
+ },
1784
+ {
1785
+ type: "def",
1786
+ def: " h i ",
1787
+ target: "http://www.google.com",
1788
+ title: undefined,
1789
+ },
1790
+ ]);
1791
+ });
1792
+
1793
+ it("should not allow defs to break out of a paragraph", function () {
1794
+ var parsed = implicitParse("hi [1]: there\n\n");
1795
+ validateParse(parsed, [
1796
+ {
1797
+ type: "paragraph",
1798
+ content: [
1799
+ {content: "hi ", type: "text"},
1800
+ {content: "[1", type: "text"},
1801
+ {content: "]", type: "text"},
1802
+ {content: ": there", type: "text"},
1803
+ ],
1804
+ },
1805
+ ]);
1806
+ });
1807
+
1808
+ it("should allow a group of defs next to each other", function () {
1809
+ var parsed = implicitParse(
1810
+ "[a]: # (title)\n" +
1811
+ "[b]: http://www.google.com\n" +
1812
+ "[//]: <> (hi)\n" +
1813
+ "[label]: # (there)\n" +
1814
+ "[#]: #\n" +
1815
+ "\n",
1816
+ );
1817
+ validateParse(parsed, [
1818
+ {
1819
+ type: "def",
1820
+ def: "a",
1821
+ target: "#",
1822
+ title: "title",
1823
+ },
1824
+ {
1825
+ type: "def",
1826
+ def: "b",
1827
+ target: "http://www.google.com",
1828
+ title: undefined,
1829
+ },
1830
+ {
1831
+ type: "def",
1832
+ def: "//",
1833
+ target: "",
1834
+ title: "hi",
1835
+ },
1836
+ {
1837
+ type: "def",
1838
+ def: "label",
1839
+ target: "#",
1840
+ title: "there",
1841
+ },
1842
+ {
1843
+ type: "def",
1844
+ def: "#",
1845
+ target: "#",
1846
+ title: undefined,
1847
+ },
1848
+ ]);
1849
+ });
1850
+
1851
+ it("should parse a single top-level paragraph", function () {
1852
+ var parsed = blockParse("hi\n\n");
1853
+ validateParse(parsed, [
1854
+ {
1855
+ type: "paragraph",
1856
+ content: [
1857
+ {
1858
+ type: "text",
1859
+ content: "hi",
1860
+ },
1861
+ ],
1862
+ },
1863
+ ]);
1864
+ });
1865
+
1866
+ it("should parse multiple top-level paragraphs", function () {
1867
+ var parsed = blockParse("hi\n\nbye\n\nthere\n\n");
1868
+ validateParse(parsed, [
1869
+ {
1870
+ type: "paragraph",
1871
+ content: [
1872
+ {
1873
+ type: "text",
1874
+ content: "hi",
1875
+ },
1876
+ ],
1877
+ },
1878
+ {
1879
+ type: "paragraph",
1880
+ content: [
1881
+ {
1882
+ type: "text",
1883
+ content: "bye",
1884
+ },
1885
+ ],
1886
+ },
1887
+ {
1888
+ type: "paragraph",
1889
+ content: [
1890
+ {
1891
+ type: "text",
1892
+ content: "there",
1893
+ },
1894
+ ],
1895
+ },
1896
+ ]);
1897
+ });
1898
+
1899
+ it("should not parse single newlines as paragraphs", function () {
1900
+ var parsed = inlineParse("hi\nbye\nthere\n");
1901
+ validateParse(parsed, [
1902
+ {
1903
+ type: "text",
1904
+ content: "hi\nbye\nthere\n",
1905
+ },
1906
+ ]);
1907
+ });
1908
+
1909
+ it("should not parse a single newline as a new paragraph", function () {
1910
+ var parsed = blockParse("hi\nbye\nthere\n\n");
1911
+ validateParse(parsed, [
1912
+ {
1913
+ type: "paragraph",
1914
+ content: [
1915
+ {
1916
+ type: "text",
1917
+ content: "hi\nbye\nthere",
1918
+ },
1919
+ ],
1920
+ },
1921
+ ]);
1922
+ });
1923
+
1924
+ it("should allow whitespace-only lines to end paragraphs", function () {
1925
+ var parsed = blockParse("hi\n \n");
1926
+ validateParse(parsed, [
1927
+ {
1928
+ type: "paragraph",
1929
+ content: [
1930
+ {
1931
+ type: "text",
1932
+ content: "hi",
1933
+ },
1934
+ ],
1935
+ },
1936
+ ]);
1937
+
1938
+ var parsed2 = blockParse("hi\n \n");
1939
+ validateParse(parsed2, [
1940
+ {
1941
+ type: "paragraph",
1942
+ content: [
1943
+ {
1944
+ type: "text",
1945
+ content: "hi",
1946
+ },
1947
+ ],
1948
+ },
1949
+ ]);
1950
+
1951
+ var parsed3 = blockParse("hi\n\n \n \n");
1952
+ validateParse(parsed3, [
1953
+ {
1954
+ type: "paragraph",
1955
+ content: [
1956
+ {
1957
+ type: "text",
1958
+ content: "hi",
1959
+ },
1960
+ ],
1961
+ },
1962
+ ]);
1963
+
1964
+ var parsed4 = blockParse("hi\n \n\n \nbye\n\n");
1965
+ validateParse(parsed4, [
1966
+ {
1967
+ type: "paragraph",
1968
+ content: [
1969
+ {
1970
+ type: "text",
1971
+ content: "hi",
1972
+ },
1973
+ ],
1974
+ },
1975
+ {
1976
+ type: "paragraph",
1977
+ content: [
1978
+ {
1979
+ type: "text",
1980
+ content: "bye",
1981
+ },
1982
+ ],
1983
+ },
1984
+ ]);
1985
+ });
1986
+
1987
+ it("should parse a single heading", function () {
1988
+ var parsed = blockParse("### heading3\n\n");
1989
+ validateParse(parsed, [
1990
+ {
1991
+ type: "heading",
1992
+ level: 3,
1993
+ content: [
1994
+ {
1995
+ type: "text",
1996
+ content: "heading3",
1997
+ },
1998
+ ],
1999
+ },
2000
+ ]);
2001
+ });
2002
+
2003
+ it("should parse a single lheading", function () {
2004
+ var parsed = blockParse("heading2\n-----\n\n");
2005
+ validateParse(parsed, [
2006
+ {
2007
+ type: "heading",
2008
+ level: 2,
2009
+ content: [
2010
+ {
2011
+ type: "text",
2012
+ content: "heading2",
2013
+ },
2014
+ ],
2015
+ },
2016
+ ]);
2017
+ });
2018
+
2019
+ it("should not parse a single lheading with two -- or ==", function () {
2020
+ var parsed = blockParse("heading1\n==\n\n");
2021
+ validateParse(parsed, [
2022
+ {
2023
+ type: "paragraph",
2024
+ content: [
2025
+ {type: "text", content: "heading1\n"},
2026
+ {type: "text", content: "="},
2027
+ {type: "text", content: "="},
2028
+ ],
2029
+ },
2030
+ ]);
2031
+
2032
+ var parsed2 = blockParse("heading2\n--\n\n");
2033
+ validateParse(parsed2, [
2034
+ {
2035
+ type: "paragraph",
2036
+ content: [
2037
+ {type: "text", content: "heading2\n"},
2038
+ {type: "text", content: "-"},
2039
+ {type: "text", content: "-"},
2040
+ ],
2041
+ },
2042
+ ]);
2043
+ });
2044
+
2045
+ it("should not parse 7 #s as an h7", function () {
2046
+ var parsed = blockParse("#######heading7\n\n");
2047
+ validateParse(parsed, [
2048
+ {
2049
+ type: "heading",
2050
+ level: 6,
2051
+ content: [
2052
+ {
2053
+ type: "text",
2054
+ content: "#heading7",
2055
+ },
2056
+ ],
2057
+ },
2058
+ ]);
2059
+ });
2060
+
2061
+ it("should parse a heading between paragraphs", function () {
2062
+ var parsed = blockParse(
2063
+ "para 1\n\n" + "#heading\n\n\n" + "para 2\n\n",
2064
+ );
2065
+ validateParse(parsed, [
2066
+ {
2067
+ type: "paragraph",
2068
+ content: [
2069
+ {
2070
+ type: "text",
2071
+ content: "para 1",
2072
+ },
2073
+ ],
2074
+ },
2075
+ {
2076
+ type: "heading",
2077
+ level: 1,
2078
+ content: [
2079
+ {
2080
+ type: "text",
2081
+ content: "heading",
2082
+ },
2083
+ ],
2084
+ },
2085
+ {
2086
+ type: "paragraph",
2087
+ content: [
2088
+ {
2089
+ type: "text",
2090
+ content: "para 2",
2091
+ },
2092
+ ],
2093
+ },
2094
+ ]);
2095
+ });
2096
+
2097
+ it("should not allow headings mid-paragraph", function () {
2098
+ var parsed = blockParse(
2099
+ "paragraph # text\n" + "more paragraph\n\n",
2100
+ );
2101
+ validateParse(parsed, [
2102
+ {
2103
+ type: "paragraph",
2104
+ content: [
2105
+ {content: "paragraph ", type: "text"},
2106
+ {content: "# text\nmore paragraph", type: "text"},
2107
+ ],
2108
+ },
2109
+ ]);
2110
+
2111
+ var parsed2 = blockParse(
2112
+ "paragraph\n" + "text\n" + "----\n" + "more paragraph\n\n",
2113
+ );
2114
+ validateParse(parsed2, [
2115
+ {
2116
+ type: "paragraph",
2117
+ content: [
2118
+ {content: "paragraph\ntext\n", type: "text"},
2119
+ {content: "-", type: "text"},
2120
+ {content: "-", type: "text"},
2121
+ {content: "-", type: "text"},
2122
+ {content: "-\nmore paragraph", type: "text"},
2123
+ ],
2124
+ },
2125
+ ]);
2126
+ });
2127
+
2128
+ it("should parse a single top-level blockquote", function () {
2129
+ var parsed = blockParse("> blockquote\n\n");
2130
+ validateParse(parsed, [
2131
+ {
2132
+ type: "blockQuote",
2133
+ content: [
2134
+ {
2135
+ type: "paragraph",
2136
+ content: [
2137
+ {
2138
+ type: "text",
2139
+ content: "blockquote",
2140
+ },
2141
+ ],
2142
+ },
2143
+ ],
2144
+ },
2145
+ ]);
2146
+ });
2147
+
2148
+ it("should parse multiple blockquotes and paragraphs", function () {
2149
+ var parsed = blockParse(
2150
+ "para 1\n\n" +
2151
+ "> blockquote 1\n" +
2152
+ ">\n" +
2153
+ ">blockquote 2\n\n" +
2154
+ "para 2\n\n",
2155
+ );
2156
+ validateParse(parsed, [
2157
+ {
2158
+ type: "paragraph",
2159
+ content: [
2160
+ {
2161
+ type: "text",
2162
+ content: "para 1",
2163
+ },
2164
+ ],
2165
+ },
2166
+ {
2167
+ type: "blockQuote",
2168
+ content: [
2169
+ {
2170
+ type: "paragraph",
2171
+ content: [
2172
+ {
2173
+ type: "text",
2174
+ content: "blockquote 1",
2175
+ },
2176
+ ],
2177
+ },
2178
+ {
2179
+ type: "paragraph",
2180
+ content: [
2181
+ {
2182
+ type: "text",
2183
+ content: "blockquote 2",
2184
+ },
2185
+ ],
2186
+ },
2187
+ ],
2188
+ },
2189
+ {
2190
+ type: "paragraph",
2191
+ content: [
2192
+ {
2193
+ type: "text",
2194
+ content: "para 2",
2195
+ },
2196
+ ],
2197
+ },
2198
+ ]);
2199
+ });
2200
+
2201
+ it("should not let a > escape a paragraph as a blockquote", function () {
2202
+ var parsed = blockParse("para 1 > not a quote\n\n");
2203
+ validateParse(parsed, [
2204
+ {
2205
+ type: "paragraph",
2206
+ content: [
2207
+ {content: "para 1 ", type: "text"},
2208
+ {content: "> not a quote", type: "text"},
2209
+ ],
2210
+ },
2211
+ ]);
2212
+ });
2213
+
2214
+ it("should parse a single top-level code block", function () {
2215
+ var parsed = blockParse(" if (true) { code(); }\n\n");
2216
+ validateParse(parsed, [
2217
+ {
2218
+ type: "codeBlock",
2219
+ lang: undefined,
2220
+ content: "if (true) { code(); }",
2221
+ },
2222
+ ]);
2223
+ });
2224
+
2225
+ it("should parse a code block with trailing spaces", function () {
2226
+ var parsed = blockParse(" if (true) { code(); }\n \n\n");
2227
+ validateParse(parsed, [
2228
+ {
2229
+ type: "codeBlock",
2230
+ lang: undefined,
2231
+ content: "if (true) { code(); }",
2232
+ },
2233
+ ]);
2234
+ });
2235
+
2236
+ it("should parse fence blocks", function () {
2237
+ var parsed = blockParse("```\ncode\n```\n\n");
2238
+ validateParse(parsed, [
2239
+ {
2240
+ type: "codeBlock",
2241
+ lang: undefined,
2242
+ content: "code",
2243
+ },
2244
+ ]);
2245
+
2246
+ var parsed2 = blockParse(
2247
+ "```aletheia\n" + "if true [code()]\n" + "```\n\n",
2248
+ );
2249
+ validateParse(parsed2, [
2250
+ {
2251
+ type: "codeBlock",
2252
+ lang: "aletheia",
2253
+ content: "if true [code()]",
2254
+ },
2255
+ ]);
2256
+ });
2257
+
2258
+ it("should allow indentation inside code blocks", function () {
2259
+ var parsed = blockParse(
2260
+ "```\n" +
2261
+ "if (true === false) {\n" +
2262
+ " throw 'world does not exist';\n" +
2263
+ "}\n" +
2264
+ "```\n\n",
2265
+ );
2266
+ validateParse(parsed, [
2267
+ {
2268
+ type: "codeBlock",
2269
+ lang: undefined,
2270
+ content:
2271
+ "if (true === false) {\n" +
2272
+ " throw 'world does not exist';\n" +
2273
+ "}",
2274
+ },
2275
+ ]);
2276
+
2277
+ var parsed = blockParse(
2278
+ "~~~\n" + " this should be indented\n" + "~~~\n\n",
2279
+ );
2280
+ validateParse(parsed, [
2281
+ {
2282
+ type: "codeBlock",
2283
+ lang: undefined,
2284
+ content: " this should be indented",
2285
+ },
2286
+ ]);
2287
+ });
2288
+
2289
+ it("should parse mixed paragraphs and code", function () {
2290
+ var parsed = blockParse(
2291
+ "this is regular text\n\n" +
2292
+ " this is code\n\n" +
2293
+ "this is more regular text\n\n",
2294
+ );
2295
+ validateParse(parsed, [
2296
+ {
2297
+ type: "paragraph",
2298
+ content: [
2299
+ {
2300
+ type: "text",
2301
+ content: "this is regular text",
2302
+ },
2303
+ ],
2304
+ },
2305
+ {
2306
+ type: "codeBlock",
2307
+ lang: undefined,
2308
+ content: "this is code",
2309
+ },
2310
+ {
2311
+ type: "paragraph",
2312
+ content: [
2313
+ {
2314
+ type: "text",
2315
+ content: "this is more regular text",
2316
+ },
2317
+ ],
2318
+ },
2319
+ ]);
2320
+ });
2321
+
2322
+ it("should parse top-level horizontal rules", function () {
2323
+ var parsed = blockParse(
2324
+ "---\n\n" +
2325
+ "***\n\n" +
2326
+ "___\n\n" +
2327
+ " - - - - \n\n" +
2328
+ "_ _ _\n\n" +
2329
+ " *** \n\n",
2330
+ );
2331
+ validateParse(parsed, [
2332
+ {type: "hr"},
2333
+ {type: "hr"},
2334
+ {type: "hr"},
2335
+ {type: "hr"},
2336
+ {type: "hr"},
2337
+ {type: "hr"},
2338
+ ]);
2339
+ });
2340
+
2341
+ it("should parse hrs between paragraphs", function () {
2342
+ var parsed = blockParse(
2343
+ "para 1\n\n" + " * * * \n\n" + "para 2\n\n",
2344
+ );
2345
+ validateParse(parsed, [
2346
+ {
2347
+ type: "paragraph",
2348
+ content: [
2349
+ {
2350
+ type: "text",
2351
+ content: "para 1",
2352
+ },
2353
+ ],
2354
+ },
2355
+ {type: "hr"},
2356
+ {
2357
+ type: "paragraph",
2358
+ content: [
2359
+ {
2360
+ type: "text",
2361
+ content: "para 2",
2362
+ },
2363
+ ],
2364
+ },
2365
+ ]);
2366
+ });
2367
+
2368
+ it("should not allow hrs within a paragraph", function () {
2369
+ var parsed = blockParse("paragraph ----\n" + "more paragraph\n\n");
2370
+ validateParse(parsed, [
2371
+ {
2372
+ type: "paragraph",
2373
+ content: [
2374
+ {content: "paragraph ", type: "text"},
2375
+ {content: "-", type: "text"},
2376
+ {content: "-", type: "text"},
2377
+ {content: "-", type: "text"},
2378
+ {content: "-\nmore paragraph", type: "text"},
2379
+ ],
2380
+ },
2381
+ ]);
2382
+ });
2383
+
2384
+ it("should parse simple unordered lists", function () {
2385
+ var parsed = blockParse(" * hi\n" + " * bye\n" + " * there\n\n");
2386
+ validateParse(parsed, [
2387
+ {
2388
+ ordered: false,
2389
+ start: undefined,
2390
+ items: [
2391
+ [
2392
+ {
2393
+ content: "hi",
2394
+ type: "text",
2395
+ },
2396
+ ],
2397
+ [
2398
+ {
2399
+ content: "bye",
2400
+ type: "text",
2401
+ },
2402
+ ],
2403
+ [
2404
+ {
2405
+ content: "there",
2406
+ type: "text",
2407
+ },
2408
+ ],
2409
+ ],
2410
+ type: "list",
2411
+ },
2412
+ ]);
2413
+ });
2414
+
2415
+ it("should parse simple ordered lists", function () {
2416
+ var parsed = blockParse(
2417
+ "1. first\n" + "2. second\n" + "3. third\n\n",
2418
+ );
2419
+ validateParse(parsed, [
2420
+ {
2421
+ type: "list",
2422
+ ordered: true,
2423
+ start: 1,
2424
+ items: [
2425
+ [
2426
+ {
2427
+ type: "text",
2428
+ content: "first",
2429
+ },
2430
+ ],
2431
+ [
2432
+ {
2433
+ type: "text",
2434
+ content: "second",
2435
+ },
2436
+ ],
2437
+ [
2438
+ {
2439
+ type: "text",
2440
+ content: "third",
2441
+ },
2442
+ ],
2443
+ ],
2444
+ },
2445
+ ]);
2446
+ });
2447
+
2448
+ it("should parse simple ordered lists with silly numbers", function () {
2449
+ var parsed = blockParse(
2450
+ "1. first\n" + "13. second\n" + "9. third\n\n",
2451
+ );
2452
+ validateParse(parsed, [
2453
+ {
2454
+ type: "list",
2455
+ start: 1,
2456
+ ordered: true,
2457
+ items: [
2458
+ [
2459
+ {
2460
+ type: "text",
2461
+ content: "first",
2462
+ },
2463
+ ],
2464
+ [
2465
+ {
2466
+ type: "text",
2467
+ content: "second",
2468
+ },
2469
+ ],
2470
+ [
2471
+ {
2472
+ type: "text",
2473
+ content: "third",
2474
+ },
2475
+ ],
2476
+ ],
2477
+ },
2478
+ ]);
2479
+
2480
+ var parsed2 = blockParse(
2481
+ "63. first\n" + "13. second\n" + "9. third\n\n",
2482
+ );
2483
+ validateParse(parsed2, [
2484
+ {
2485
+ type: "list",
2486
+ start: 63,
2487
+ ordered: true,
2488
+ items: [
2489
+ [
2490
+ {
2491
+ type: "text",
2492
+ content: "first",
2493
+ },
2494
+ ],
2495
+ [
2496
+ {
2497
+ type: "text",
2498
+ content: "second",
2499
+ },
2500
+ ],
2501
+ [
2502
+ {
2503
+ type: "text",
2504
+ content: "third",
2505
+ },
2506
+ ],
2507
+ ],
2508
+ },
2509
+ ]);
2510
+ });
2511
+
2512
+ it("should parse nested lists", function () {
2513
+ var parsed = blockParse(
2514
+ "1. first\n" +
2515
+ "2. second\n" +
2516
+ " * inner\n" +
2517
+ " * inner\n" +
2518
+ "3. third\n\n",
2519
+ );
2520
+ validateParse(parsed, [
2521
+ {
2522
+ ordered: true,
2523
+ start: 1,
2524
+ items: [
2525
+ [
2526
+ {
2527
+ content: "first",
2528
+ type: "text",
2529
+ },
2530
+ ],
2531
+ [
2532
+ {
2533
+ content: "second\n",
2534
+ type: "text",
2535
+ },
2536
+ {
2537
+ ordered: false,
2538
+ start: undefined,
2539
+ items: [
2540
+ [
2541
+ {
2542
+ content: "inner",
2543
+ type: "text",
2544
+ },
2545
+ ],
2546
+ [
2547
+ {
2548
+ content: "inner",
2549
+ type: "text",
2550
+ },
2551
+ ],
2552
+ ],
2553
+ type: "list",
2554
+ },
2555
+ ],
2556
+ [
2557
+ {
2558
+ content: "third",
2559
+ type: "text",
2560
+ },
2561
+ ],
2562
+ ],
2563
+ type: "list",
2564
+ },
2565
+ ]);
2566
+
2567
+ var parsed = blockParse(
2568
+ " * hi\n" + " * bye\n" + " * there\n\n",
2569
+ );
2570
+ validateParse(parsed, [
2571
+ {
2572
+ ordered: false,
2573
+ start: undefined,
2574
+ items: [
2575
+ [
2576
+ {
2577
+ content: "hi\n ", // NOTE(aria): The extra space here is
2578
+ type: "text", // weird and we should consider fixing
2579
+ },
2580
+ {
2581
+ ordered: false,
2582
+ start: undefined,
2583
+ items: [
2584
+ [
2585
+ {
2586
+ content: "bye",
2587
+ type: "text",
2588
+ },
2589
+ ],
2590
+ [
2591
+ {
2592
+ content: "there",
2593
+ type: "text",
2594
+ },
2595
+ ],
2596
+ ],
2597
+ type: "list",
2598
+ },
2599
+ ],
2600
+ ],
2601
+ type: "list",
2602
+ },
2603
+ ]);
2604
+ });
2605
+
2606
+ it("should parse loose lists", function () {
2607
+ var parsed = blockParse(
2608
+ " * hi\n\n" + " * bye\n\n" + " * there\n\n",
2609
+ );
2610
+ validateParse(parsed, [
2611
+ {
2612
+ type: "list",
2613
+ ordered: false,
2614
+ start: undefined,
2615
+ items: [
2616
+ [
2617
+ {
2618
+ type: "paragraph",
2619
+ content: [
2620
+ {
2621
+ type: "text",
2622
+ content: "hi",
2623
+ },
2624
+ ],
2625
+ },
2626
+ ],
2627
+ [
2628
+ {
2629
+ type: "paragraph",
2630
+ content: [
2631
+ {
2632
+ type: "text",
2633
+ content: "bye",
2634
+ },
2635
+ ],
2636
+ },
2637
+ ],
2638
+ [
2639
+ {
2640
+ type: "paragraph",
2641
+ content: [
2642
+ {
2643
+ type: "text",
2644
+ content: "there",
2645
+ },
2646
+ ],
2647
+ },
2648
+ ],
2649
+ ],
2650
+ },
2651
+ ]);
2652
+ });
2653
+
2654
+ it("should have defined behaviour for semi-loose lists", function () {
2655
+ // we mostly care that this does something vaguely reasonable.
2656
+ // if you write markdown like this the results are your own fault.
2657
+ var parsed = blockParse(" * hi\n" + " * bye\n\n" + " * there\n\n");
2658
+ validateParse(parsed, [
2659
+ {
2660
+ type: "list",
2661
+ ordered: false,
2662
+ start: undefined,
2663
+ items: [
2664
+ [
2665
+ {
2666
+ type: "text",
2667
+ content: "hi",
2668
+ },
2669
+ ],
2670
+ [
2671
+ {
2672
+ type: "paragraph",
2673
+ content: [
2674
+ {
2675
+ type: "text",
2676
+ content: "bye",
2677
+ },
2678
+ ],
2679
+ },
2680
+ ],
2681
+ [
2682
+ {
2683
+ type: "paragraph",
2684
+ content: [
2685
+ {
2686
+ type: "text",
2687
+ content: "there",
2688
+ },
2689
+ ],
2690
+ },
2691
+ ],
2692
+ ],
2693
+ },
2694
+ ]);
2695
+
2696
+ var parsed2 = blockParse(" * hi\n\n" + " * bye\n" + " * there\n\n");
2697
+ validateParse(parsed2, [
2698
+ {
2699
+ type: "list",
2700
+ ordered: false,
2701
+ start: undefined,
2702
+ items: [
2703
+ [
2704
+ {
2705
+ type: "paragraph",
2706
+ content: [
2707
+ {
2708
+ type: "text",
2709
+ content: "hi",
2710
+ },
2711
+ ],
2712
+ },
2713
+ ],
2714
+ [
2715
+ {
2716
+ type: "text",
2717
+ content: "bye",
2718
+ },
2719
+ ],
2720
+ [
2721
+ {
2722
+ type: "text",
2723
+ content: "there",
2724
+ },
2725
+ ],
2726
+ ],
2727
+ },
2728
+ ]);
2729
+ });
2730
+
2731
+ it("should parse paragraphs within loose lists", function () {
2732
+ var parsed = blockParse(
2733
+ " * hi\n\n" + " hello\n\n" + " * bye\n\n" + " * there\n\n",
2734
+ );
2735
+ validateParse(parsed, [
2736
+ {
2737
+ type: "list",
2738
+ ordered: false,
2739
+ start: undefined,
2740
+ items: [
2741
+ [
2742
+ {
2743
+ type: "paragraph",
2744
+ content: [
2745
+ {
2746
+ type: "text",
2747
+ content: "hi",
2748
+ },
2749
+ ],
2750
+ },
2751
+ {
2752
+ type: "paragraph",
2753
+ content: [
2754
+ {
2755
+ type: "text",
2756
+ content: "hello",
2757
+ },
2758
+ ],
2759
+ },
2760
+ ],
2761
+ [
2762
+ {
2763
+ type: "paragraph",
2764
+ content: [
2765
+ {
2766
+ type: "text",
2767
+ content: "bye",
2768
+ },
2769
+ ],
2770
+ },
2771
+ ],
2772
+ [
2773
+ {
2774
+ type: "paragraph",
2775
+ content: [
2776
+ {
2777
+ type: "text",
2778
+ content: "there",
2779
+ },
2780
+ ],
2781
+ },
2782
+ ],
2783
+ ],
2784
+ },
2785
+ ]);
2786
+ });
2787
+
2788
+ it("should allow line breaks+wrapping in tight lists", function () {
2789
+ var parsed = blockParse(
2790
+ " * hi\n" + " hello\n\n" + " * bye\n\n" + " * there\n\n",
2791
+ );
2792
+ validateParse(parsed, [
2793
+ {
2794
+ type: "list",
2795
+ ordered: false,
2796
+ start: undefined,
2797
+ items: [
2798
+ [
2799
+ {
2800
+ type: "paragraph",
2801
+ content: [
2802
+ {
2803
+ type: "text",
2804
+ content: "hi\nhello",
2805
+ },
2806
+ ],
2807
+ },
2808
+ ],
2809
+ [
2810
+ {
2811
+ type: "paragraph",
2812
+ content: [
2813
+ {
2814
+ type: "text",
2815
+ content: "bye",
2816
+ },
2817
+ ],
2818
+ },
2819
+ ],
2820
+ [
2821
+ {
2822
+ type: "paragraph",
2823
+ content: [
2824
+ {
2825
+ type: "text",
2826
+ content: "there",
2827
+ },
2828
+ ],
2829
+ },
2830
+ ],
2831
+ ],
2832
+ },
2833
+ ]);
2834
+ });
2835
+
2836
+ it("should allow code inside list items", function () {
2837
+ var parsed = blockParse(
2838
+ " * this is a list\n\n" + " with code in it\n\n",
2839
+ );
2840
+ validateParse(parsed, [
2841
+ {
2842
+ type: "list",
2843
+ ordered: false,
2844
+ start: undefined,
2845
+ items: [
2846
+ [
2847
+ {
2848
+ type: "paragraph",
2849
+ content: [
2850
+ {
2851
+ type: "text",
2852
+ content: "this is a list",
2853
+ },
2854
+ ],
2855
+ },
2856
+ {
2857
+ type: "codeBlock",
2858
+ lang: undefined,
2859
+ content: "with code in it",
2860
+ },
2861
+ ],
2862
+ ],
2863
+ },
2864
+ ]);
2865
+
2866
+ var parsed2 = blockParse(
2867
+ " * this is a list\n\n" +
2868
+ " with code in it\n\n" +
2869
+ " * second item\n" +
2870
+ "\n",
2871
+ );
2872
+ validateParse(parsed2, [
2873
+ {
2874
+ type: "list",
2875
+ ordered: false,
2876
+ start: undefined,
2877
+ items: [
2878
+ [
2879
+ {
2880
+ type: "paragraph",
2881
+ content: [
2882
+ {
2883
+ type: "text",
2884
+ content: "this is a list",
2885
+ },
2886
+ ],
2887
+ },
2888
+ {
2889
+ type: "codeBlock",
2890
+ lang: undefined,
2891
+ content: "with code in it",
2892
+ },
2893
+ ],
2894
+ [
2895
+ {
2896
+ type: "paragraph",
2897
+ content: [
2898
+ {
2899
+ type: "text",
2900
+ content: "second item",
2901
+ },
2902
+ ],
2903
+ },
2904
+ ],
2905
+ ],
2906
+ },
2907
+ ]);
2908
+ });
2909
+
2910
+ it("should allow lists inside blockquotes", function () {
2911
+ // This list also has lots of trailing space after the *s
2912
+ var parsed = blockParse(
2913
+ "> A list within a blockquote\n" +
2914
+ ">\n" +
2915
+ "> * asterisk 1\n" +
2916
+ "> * asterisk 2\n" +
2917
+ "> * asterisk 3\n" +
2918
+ "\n",
2919
+ );
2920
+ validateParse(parsed, [
2921
+ {
2922
+ type: "blockQuote",
2923
+ content: [
2924
+ {
2925
+ type: "paragraph",
2926
+ content: [
2927
+ {
2928
+ content: "A list within a blockquote",
2929
+ type: "text",
2930
+ },
2931
+ ],
2932
+ },
2933
+ {
2934
+ type: "list",
2935
+ ordered: false,
2936
+ start: undefined,
2937
+ items: [
2938
+ [
2939
+ {
2940
+ content: "asterisk 1",
2941
+ type: "text",
2942
+ },
2943
+ ],
2944
+ [
2945
+ {
2946
+ content: "asterisk 2",
2947
+ type: "text",
2948
+ },
2949
+ ],
2950
+ [
2951
+ {
2952
+ content: "asterisk 3",
2953
+ type: "text",
2954
+ },
2955
+ ],
2956
+ ],
2957
+ },
2958
+ ],
2959
+ },
2960
+ ]);
2961
+ });
2962
+
2963
+ it("symbols should not break a paragraph into a list", function () {
2964
+ var parsed = blockParse("hi - there\n\n");
2965
+ validateParse(parsed, [
2966
+ {
2967
+ type: "paragraph",
2968
+ content: [
2969
+ {content: "hi ", type: "text"},
2970
+ {content: "- there", type: "text"},
2971
+ ],
2972
+ },
2973
+ ]);
2974
+
2975
+ var parsed2 = blockParse("hi * there\n\n");
2976
+ validateParse(parsed2, [
2977
+ {
2978
+ type: "paragraph",
2979
+ content: [
2980
+ {content: "hi ", type: "text"},
2981
+ {content: "* there", type: "text"},
2982
+ ],
2983
+ },
2984
+ ]);
2985
+
2986
+ var parsed3 = blockParse("hi 1. there\n\n");
2987
+ validateParse(parsed3, [
2988
+ {
2989
+ type: "paragraph",
2990
+ content: [
2991
+ {content: "hi 1", type: "text"},
2992
+ {content: ". there", type: "text"},
2993
+ ],
2994
+ },
2995
+ ]);
2996
+ });
2997
+
2998
+ it("dashes or numbers should not break a list item into a list", function () {
2999
+ var parsed = blockParse("- hi - there\n\n");
3000
+ validateParse(parsed, [
3001
+ {
3002
+ type: "list",
3003
+ ordered: false,
3004
+ start: undefined,
3005
+ items: [
3006
+ [
3007
+ {content: "hi ", type: "text"},
3008
+ {content: "- there", type: "text"},
3009
+ ],
3010
+ ],
3011
+ },
3012
+ ]);
3013
+
3014
+ var parsed2 = blockParse("* hi * there\n\n");
3015
+ validateParse(parsed2, [
3016
+ {
3017
+ type: "list",
3018
+ ordered: false,
3019
+ start: undefined,
3020
+ items: [
3021
+ [
3022
+ {content: "hi ", type: "text"},
3023
+ {content: "* there", type: "text"},
3024
+ ],
3025
+ ],
3026
+ },
3027
+ ]);
3028
+
3029
+ var parsed3 = blockParse("1. hi 1. there\n\n");
3030
+ validateParse(parsed3, [
3031
+ {
3032
+ type: "list",
3033
+ ordered: true,
3034
+ start: 1,
3035
+ items: [
3036
+ [
3037
+ {content: "hi 1", type: "text"},
3038
+ {content: ". there", type: "text"},
3039
+ ],
3040
+ ],
3041
+ },
3042
+ ]);
3043
+ });
3044
+
3045
+ it("should ignore double spaces at the end of lists", function () {
3046
+ var parsed = blockParse(" * hi \n * there\n\n");
3047
+ validateParse(parsed, [
3048
+ {
3049
+ type: "list",
3050
+ ordered: false,
3051
+ start: undefined,
3052
+ items: [
3053
+ [{type: "text", content: "hi"}],
3054
+ [{type: "text", content: "there"}],
3055
+ ],
3056
+ },
3057
+ ]);
3058
+ });
3059
+
3060
+ it("should parse very simple tables", function () {
3061
+ var expected = [
3062
+ {
3063
+ type: "table",
3064
+ header: [
3065
+ [{type: "text", content: "h1"}],
3066
+ [{type: "text", content: "h2"}],
3067
+ [{type: "text", content: "h3"}],
3068
+ ],
3069
+ align: /** @type {Array<SimpleMarkdown.TableAlignment>} */ ([
3070
+ null,
3071
+ null,
3072
+ null,
3073
+ ]),
3074
+ cells: [
3075
+ [
3076
+ [{type: "text", content: "d1"}],
3077
+ [{type: "text", content: "d2"}],
3078
+ [{type: "text", content: "d3"}],
3079
+ ],
3080
+ [
3081
+ [{type: "text", content: "e1"}],
3082
+ [{type: "text", content: "e2"}],
3083
+ [{type: "text", content: "e3"}],
3084
+ ],
3085
+ ],
3086
+ },
3087
+ ];
3088
+
3089
+ var parsedPiped = blockParse(
3090
+ "| h1 | h2 | h3 |\n" +
3091
+ "| -- | -- | -- |\n" +
3092
+ "| d1 | d2 | d3 |\n" +
3093
+ "| e1 | e2 | e3 |\n" +
3094
+ "\n",
3095
+ );
3096
+ validateParse(parsedPiped, expected);
3097
+
3098
+ var parsedNp = blockParse(
3099
+ "h1 | h2 | h3\n" +
3100
+ "- | - | -\n" +
3101
+ "d1 | d2 | d3\n" +
3102
+ "e1 | e2 | e3\n" +
3103
+ "\n",
3104
+ );
3105
+ validateParse(parsedNp, expected);
3106
+ });
3107
+
3108
+ it("should parse inside table contents", function () {
3109
+ var expected = [
3110
+ {
3111
+ type: "table",
3112
+ header: [
3113
+ [
3114
+ {
3115
+ type: "em",
3116
+ content: [{type: "text", content: "h1"}],
3117
+ },
3118
+ ],
3119
+ [
3120
+ {
3121
+ type: "em",
3122
+ content: [{type: "text", content: "h2"}],
3123
+ },
3124
+ ],
3125
+ [
3126
+ {
3127
+ type: "em",
3128
+ content: [{type: "text", content: "h3"}],
3129
+ },
3130
+ ],
3131
+ ],
3132
+ align: /** @type {Array<SimpleMarkdown.TableAlignment>} */ ([
3133
+ null,
3134
+ null,
3135
+ null,
3136
+ ]),
3137
+ cells: [
3138
+ [
3139
+ [
3140
+ {
3141
+ type: "em",
3142
+ content: [{type: "text", content: "d1"}],
3143
+ },
3144
+ ],
3145
+ [
3146
+ {
3147
+ type: "em",
3148
+ content: [{type: "text", content: "d2"}],
3149
+ },
3150
+ ],
3151
+ [
3152
+ {
3153
+ type: "em",
3154
+ content: [{type: "text", content: "d3"}],
3155
+ },
3156
+ ],
3157
+ ],
3158
+ ],
3159
+ },
3160
+ ];
3161
+
3162
+ var parsedPiped = blockParse(
3163
+ "| *h1* | *h2* | *h3* |\n" +
3164
+ "| ---- | ---- | ---- |\n" +
3165
+ "| *d1* | *d2* | *d3* |\n" +
3166
+ "\n",
3167
+ );
3168
+ validateParse(parsedPiped, expected);
3169
+
3170
+ var parsedNp = blockParse(
3171
+ "*h1* | *h2* | *h3*\n" +
3172
+ "-|-|-\n" +
3173
+ "*d1* | *d2* | *d3*\n" +
3174
+ "\n",
3175
+ );
3176
+ validateParse(parsedNp, expected);
3177
+ });
3178
+
3179
+ it("should parse table alignments", function () {
3180
+ /**
3181
+ * @param {string} tableSrc
3182
+ * @param {Array<SimpleMarkdown.TableAlignment>} expectedAligns
3183
+ */
3184
+ var validateAligns = function (tableSrc, expectedAligns) {
3185
+ var parsed = blockParse(tableSrc + "\n");
3186
+ assert.strictEqual(parsed[0].type, "table");
3187
+ var actualAligns = parsed[0].align;
3188
+ validateParse(actualAligns, expectedAligns);
3189
+ };
3190
+
3191
+ validateAligns(
3192
+ "| h1 | h2 | h3 |\n" +
3193
+ "| -- | -- | -- |\n" +
3194
+ "| d1 | d2 | d3 |\n",
3195
+ /** @type {Array<SimpleMarkdown.TableAlignment>} */ ([
3196
+ null,
3197
+ null,
3198
+ null,
3199
+ ]),
3200
+ );
3201
+
3202
+ validateAligns(
3203
+ "| h1 | h2 | h3 |\n" +
3204
+ "|:--:|:-: | :-: |\n" +
3205
+ "| d1 | d2 | d3 |\n",
3206
+ ["center", "center", "center"],
3207
+ );
3208
+
3209
+ validateAligns(
3210
+ "| h1 | h2 | h3 |\n" +
3211
+ "| :- |:---| :--|\n" +
3212
+ "| d1 | d2 | d3 |\n",
3213
+ ["left", "left", "left"],
3214
+ );
3215
+
3216
+ validateAligns(
3217
+ "| h1 | h2 | h3 |\n" +
3218
+ "| -: |-: | -:|\n" +
3219
+ "| d1 | d2 | d3 |\n",
3220
+ ["right", "right", "right"],
3221
+ );
3222
+
3223
+ validateAligns(
3224
+ "h1 | h2 | h3\n" + ":-|:-:|-:\n" + "d1 | d2 | d3\n",
3225
+ ["left", "center", "right"],
3226
+ );
3227
+
3228
+ validateAligns(
3229
+ "h1 | h2 | h3\n" + " :---: |:--| --:\n" + "d1 | d2 | d3\n",
3230
+ ["center", "left", "right"],
3231
+ );
3232
+ });
3233
+
3234
+ it("should parse empty table cells", function () {
3235
+ var expected = [
3236
+ {
3237
+ type: "table",
3238
+ header: /** @type {any[][]} */ ([[], [], []]),
3239
+ align: /** @type {Array<SimpleMarkdown.TableAlignment>} */ ([
3240
+ null,
3241
+ null,
3242
+ null,
3243
+ ]),
3244
+ cells: /** @type {any[][]} */ ([
3245
+ [[], [], []],
3246
+ [[], [], []],
3247
+ ]),
3248
+ },
3249
+ ];
3250
+
3251
+ var parsedPiped = blockParse(
3252
+ "| | | |\n" +
3253
+ "| -- | -- | -- |\n" +
3254
+ "| | | |\n" +
3255
+ "| | | |\n" +
3256
+ "\n",
3257
+ );
3258
+ validateParse(parsedPiped, expected);
3259
+
3260
+ var parsedNp = blockParse(
3261
+ " | | \n" +
3262
+ "- | - | -\n" +
3263
+ " | | \n" +
3264
+ " | | \n" +
3265
+ "\n",
3266
+ );
3267
+ validateParse(parsedNp, expected);
3268
+ });
3269
+
3270
+ it("should allow escaping pipes inside tables", function () {
3271
+ var expected = [
3272
+ {
3273
+ type: "table",
3274
+ header: [
3275
+ [
3276
+ {type: "text", content: "|"},
3277
+ {type: "text", content: "Attribute"},
3278
+ {type: "text", content: "|"},
3279
+ ],
3280
+ [
3281
+ {type: "text", content: "|"},
3282
+ {type: "text", content: "Type"},
3283
+ {type: "text", content: "|"},
3284
+ ],
3285
+ ],
3286
+ align: /** @type {Array<SimpleMarkdown.TableAlignment>} */ ([
3287
+ null,
3288
+ null,
3289
+ ]),
3290
+ cells: [
3291
+ [
3292
+ [
3293
+ {type: "text", content: "pos"},
3294
+ {type: "text", content: "|"},
3295
+ {type: "text", content: "position"},
3296
+ ],
3297
+ [
3298
+ {type: "text", content: '"left'},
3299
+ {type: "text", content: '" '},
3300
+ {type: "text", content: "|"},
3301
+ {type: "text", content: " "},
3302
+ {type: "text", content: '"right'},
3303
+ {type: "text", content: '"'},
3304
+ ],
3305
+ ],
3306
+ ],
3307
+ },
3308
+ ];
3309
+
3310
+ var parsedPiped = blockParse(
3311
+ "| \\|Attribute\\| | \\|Type\\| |\n" +
3312
+ "| --------------- | ------------------ |\n" +
3313
+ '| pos\\|position | "left" \\| "right" |\n' +
3314
+ "\n",
3315
+ );
3316
+ validateParse(parsedPiped, expected);
3317
+
3318
+ var parsedNp = blockParse(
3319
+ "\\|Attribute\\| | \\|Type\\| \n" +
3320
+ "--------------- | ------------------\n" +
3321
+ 'pos\\|position | "left" \\| "right"\n' +
3322
+ "\n",
3323
+ );
3324
+ validateParse(parsedNp, expected);
3325
+ });
3326
+
3327
+ it("should allow pipes in code inside tables", function () {
3328
+ var expected = [
3329
+ {
3330
+ type: "table",
3331
+ header: [
3332
+ [{type: "text", content: "Attribute"}],
3333
+ [{type: "text", content: "Type"}],
3334
+ ],
3335
+ align: /** @type {Array<SimpleMarkdown.TableAlignment>} */ ([
3336
+ null,
3337
+ null,
3338
+ ]),
3339
+ cells: [
3340
+ [
3341
+ [{type: "inlineCode", content: "position"}],
3342
+ [{type: "inlineCode", content: '"left" | "right"'}],
3343
+ ],
3344
+ ],
3345
+ },
3346
+ ];
3347
+
3348
+ var parsedPiped = blockParse(
3349
+ "| Attribute | Type |\n" +
3350
+ "| ------------ | --------------------- |\n" +
3351
+ '| `position` | `"left" | "right"` |\n' +
3352
+ "\n",
3353
+ );
3354
+ validateParse(parsedPiped, expected);
3355
+
3356
+ var parsedNp = blockParse(
3357
+ "Attribute | Type \n" +
3358
+ "------------ | ---------------------\n" +
3359
+ '`position` | `"left" | "right"`\n' +
3360
+ "\n",
3361
+ );
3362
+ validateParse(parsedNp, expected);
3363
+ });
3364
+
3365
+ it("should be able to parse <br>s", function () {
3366
+ // Inside a paragraph:
3367
+ var parsed = blockParse("hi \nbye\n\n");
3368
+ validateParse(parsed, [
3369
+ {
3370
+ type: "paragraph",
3371
+ content: [
3372
+ {content: "hi", type: "text"},
3373
+ {type: "br"},
3374
+ {content: "bye", type: "text"},
3375
+ ],
3376
+ },
3377
+ ]);
3378
+
3379
+ // Outside a paragraph:
3380
+ var parsed2 = inlineParse("hi \nbye");
3381
+ validateParse(parsed2, [
3382
+ {content: "hi", type: "text"},
3383
+ {type: "br"},
3384
+ {content: "bye", type: "text"},
3385
+ ]);
3386
+
3387
+ // But double spaces on the same line shouldn't count:
3388
+ var parsed3 = inlineParse("hi bye");
3389
+ validateParse(parsed3, [{content: "hi bye", type: "text"}]);
3390
+ });
3391
+
3392
+ it("should parse unicode characters in a word", function () {
3393
+ var parsed = inlineParse("string with parse öppurtunitiés");
3394
+ validateParse(parsed, [
3395
+ {
3396
+ type: "text",
3397
+ content: "string with parse öppurtunitiés",
3398
+ },
3399
+ ]);
3400
+ });
3401
+ });
3402
+
3403
+ describe("preprocess step", function () {
3404
+ it("should strip `\\f`s", function () {
3405
+ var parsed = blockParse("hi\n\n\fbye\n\n");
3406
+ validateParse(parsed, [
3407
+ {
3408
+ type: "paragraph",
3409
+ content: [{content: "hi", type: "text"}],
3410
+ },
3411
+ {
3412
+ type: "paragraph",
3413
+ content: [{content: "bye", type: "text"}],
3414
+ },
3415
+ ]);
3416
+
3417
+ var parsed2 = blockParse("hi\n\f\nbye\n\n");
3418
+ validateParse(parsed2, [
3419
+ {
3420
+ type: "paragraph",
3421
+ content: [{content: "hi", type: "text"}],
3422
+ },
3423
+ {
3424
+ type: "paragraph",
3425
+ content: [{content: "bye", type: "text"}],
3426
+ },
3427
+ ]);
3428
+ });
3429
+
3430
+ it("should handle \\r nicely", function () {
3431
+ var parsed = blockParse("hi\r\nbye\n\n");
3432
+ validateParse(parsed, [
3433
+ {
3434
+ type: "paragraph",
3435
+ content: [{content: "hi\nbye", type: "text"}],
3436
+ },
3437
+ ]);
3438
+
3439
+ var parsed2 = blockParse("hi\r\rbye\n\n");
3440
+ validateParse(parsed2, [
3441
+ {
3442
+ type: "paragraph",
3443
+ content: [{content: "hi", type: "text"}],
3444
+ },
3445
+ {
3446
+ type: "paragraph",
3447
+ content: [{content: "bye", type: "text"}],
3448
+ },
3449
+ ]);
3450
+ });
3451
+
3452
+ it("should treat \\t as four spaces", function () {
3453
+ var parsed = blockParse("\tcode\n\n");
3454
+ validateParse(parsed, [
3455
+ {
3456
+ type: "codeBlock",
3457
+ lang: undefined,
3458
+ content: "code",
3459
+ },
3460
+ ]);
3461
+ });
3462
+ });
3463
+
3464
+ describe("parser extension api", function () {
3465
+ it("should parse a simple %variable% extension", function () {
3466
+ var percentVarRule = {
3467
+ match: function (/** @type {string} */ source) {
3468
+ return /^%([\s\S]+?)%/.exec(source);
3469
+ },
3470
+
3471
+ order: SimpleMarkdown.defaultRules.em.order + 0.5,
3472
+
3473
+ parse: function (
3474
+ /** @type {SimpleMarkdown.Capture} */ capture,
3475
+ /** @type {SimpleMarkdown.Parser} */ parse,
3476
+ /** @type {SimpleMarkdown.State} */ state,
3477
+ ) {
3478
+ return {
3479
+ content: capture[1],
3480
+ };
3481
+ },
3482
+ };
3483
+
3484
+ var rules = Object.assign({}, SimpleMarkdown.defaultRules, {
3485
+ percentVar: percentVarRule,
3486
+ });
3487
+
3488
+ // $FlowFixMe
3489
+ var rawBuiltParser = SimpleMarkdown.parserFor(rules);
3490
+
3491
+ /** @type {SimpleMarkdown.Parser} */
3492
+ var inlineParse = function (source) {
3493
+ return rawBuiltParser(source, {inline: true});
3494
+ };
3495
+
3496
+ var parsed = inlineParse("Hi %name%!");
3497
+
3498
+ validateParse(parsed, [
3499
+ {content: "Hi ", type: "text"},
3500
+ {content: "name", type: "percentVar"},
3501
+ {content: "!", type: "text"},
3502
+ ]);
3503
+ });
3504
+
3505
+ describe("should sort rules by order and name", function () {
3506
+ var emRule = {
3507
+ match: SimpleMarkdown.inlineRegex(/^_([\s\S]+?)_/),
3508
+ parse: function (
3509
+ /** @type {SimpleMarkdown.Capture} */ capture,
3510
+ /** @type {SimpleMarkdown.Parser} */ parse,
3511
+ /** @type {SimpleMarkdown.State} */ state,
3512
+ ) {
3513
+ return {
3514
+ content: capture[1],
3515
+ };
3516
+ },
3517
+ };
3518
+ var strongRule = {
3519
+ match: SimpleMarkdown.defaultRules.strong.match,
3520
+ parse: function (
3521
+ /** @type {SimpleMarkdown.Capture} */ capture,
3522
+ /** @type {SimpleMarkdown.Parser} */ parse,
3523
+ /** @type {SimpleMarkdown.State} */ state,
3524
+ ) {
3525
+ return {
3526
+ content: capture[1],
3527
+ };
3528
+ },
3529
+ };
3530
+ var textRule = Object.assign({}, SimpleMarkdown.defaultRules.text, {
3531
+ order: 10,
3532
+ });
3533
+
3534
+ it("should sort rules by order", function () {
3535
+ // $FlowFixMe
3536
+ var parser1 = SimpleMarkdown.parserFor({
3537
+ em1: Object.assign({}, emRule, {
3538
+ order: 0,
3539
+ }),
3540
+ em2: Object.assign({}, emRule, {
3541
+ order: 1,
3542
+ }),
3543
+ text: textRule,
3544
+ });
3545
+
3546
+ var parsed1 = parser1("_hi_", {inline: true});
3547
+ validateParse(parsed1, [{content: "hi", type: "em1"}]);
3548
+
3549
+ // $FlowFixMe
3550
+ var parser2 = SimpleMarkdown.parserFor({
3551
+ em1: Object.assign({}, emRule, {
3552
+ order: 1,
3553
+ }),
3554
+ em2: Object.assign({}, emRule, {
3555
+ order: 0,
3556
+ }),
3557
+ text: textRule,
3558
+ });
3559
+
3560
+ var parsed2 = parser2("_hi_", {inline: true});
3561
+ validateParse(parsed2, [{content: "hi", type: "em2"}]);
3562
+ });
3563
+
3564
+ it("should allow fractional orders", function () {
3565
+ // $FlowFixMe
3566
+ var parser1 = SimpleMarkdown.parserFor({
3567
+ em1: Object.assign({}, emRule, {
3568
+ order: 1.4,
3569
+ }),
3570
+ em2: Object.assign({}, emRule, {
3571
+ order: 0.9,
3572
+ }),
3573
+ text: textRule,
3574
+ });
3575
+
3576
+ var parsed1 = parser1("_hi_", {inline: true});
3577
+ validateParse(parsed1, [{content: "hi", type: "em2"}]);
3578
+
3579
+ // $FlowFixMe
3580
+ var parser2 = SimpleMarkdown.parserFor({
3581
+ em1: Object.assign({}, emRule, {
3582
+ order: 0.5,
3583
+ }),
3584
+ em2: Object.assign({}, emRule, {
3585
+ order: 0,
3586
+ }),
3587
+ text: textRule,
3588
+ });
3589
+
3590
+ var parsed2 = parser2("_hi_", {inline: true});
3591
+ validateParse(parsed2, [{content: "hi", type: "em2"}]);
3592
+ });
3593
+
3594
+ it("should allow negative orders", function () {
3595
+ // $FlowFixMe
3596
+ var parser1 = SimpleMarkdown.parserFor({
3597
+ em1: Object.assign({}, emRule, {
3598
+ order: 0,
3599
+ }),
3600
+ em2: Object.assign({}, emRule, {
3601
+ order: -1,
3602
+ }),
3603
+ text: textRule,
3604
+ });
3605
+
3606
+ var parsed1 = parser1("_hi_", {inline: true});
3607
+ validateParse(parsed1, [{content: "hi", type: "em2"}]);
3608
+
3609
+ // $FlowFixMe
3610
+ var parser2 = SimpleMarkdown.parserFor({
3611
+ em1: Object.assign({}, emRule, {
3612
+ order: -2,
3613
+ }),
3614
+ em2: Object.assign({}, emRule, {
3615
+ order: 1,
3616
+ }),
3617
+ text: textRule,
3618
+ });
3619
+
3620
+ var parsed2 = parser2("_hi_", {inline: true});
3621
+ validateParse(parsed2, [{content: "hi", type: "em1"}]);
3622
+ });
3623
+
3624
+ it("should break ties by rule name", function () {
3625
+ // $FlowFixMe
3626
+ var parser1 = SimpleMarkdown.parserFor({
3627
+ em1: Object.assign({}, emRule, {
3628
+ order: 0,
3629
+ }),
3630
+ em2: Object.assign({}, emRule, {
3631
+ order: 0,
3632
+ }),
3633
+ text: textRule,
3634
+ });
3635
+
3636
+ var parsed1 = parser1("_hi_", {inline: true});
3637
+ validateParse(parsed1, [{content: "hi", type: "em1"}]);
3638
+
3639
+ // ...regardless of their order in the
3640
+ // original rule definition
3641
+ // $FlowFixMe
3642
+ var parser2 = SimpleMarkdown.parserFor({
3643
+ em2: Object.assign({}, emRule, {
3644
+ order: 0,
3645
+ }),
3646
+ em1: Object.assign({}, emRule, {
3647
+ order: 0,
3648
+ }),
3649
+ text: textRule,
3650
+ });
3651
+
3652
+ var parsed2 = parser2("_hi_", {inline: true});
3653
+ validateParse(parsed2, [{content: "hi", type: "em1"}]);
3654
+ });
3655
+
3656
+ it("should output a warning for non-numeric orders", function () {
3657
+ var oldconsolewarn = console.warn;
3658
+ /** @type {any[]} */
3659
+ var warnings = [];
3660
+ /*::FLOW_IGNORE_COVARIANCE.*/ console.warn = function (
3661
+ /** @type {any} */ warning,
3662
+ ) {
3663
+ warnings.push(warning);
3664
+ };
3665
+
3666
+ // $FlowFixMe
3667
+ var parser1 = SimpleMarkdown.parserFor({
3668
+ em1: Object.assign({}, emRule, {
3669
+ order: 1 / 0 - 1 / 0,
3670
+ }),
3671
+ text: textRule,
3672
+ });
3673
+
3674
+ assert.strictEqual(warnings.length, 1);
3675
+ assert.strictEqual(
3676
+ warnings[0],
3677
+ "simple-markdown: Invalid order for rule `em1`: NaN",
3678
+ );
3679
+
3680
+ /*::FLOW_IGNORE_COVARIANCE.*/ console.warn = oldconsolewarn;
3681
+ });
3682
+
3683
+ it("should break ties with quality", function () {
3684
+ // $FlowFixMe
3685
+ var parser1 = SimpleMarkdown.parserFor({
3686
+ em1: Object.assign({}, emRule, {
3687
+ order: 0,
3688
+ quality: function () {
3689
+ return 1;
3690
+ },
3691
+ }),
3692
+ em2: Object.assign({}, emRule, {
3693
+ order: 0,
3694
+ quality: function () {
3695
+ return 2;
3696
+ },
3697
+ }),
3698
+ text: textRule,
3699
+ });
3700
+
3701
+ var parsed1 = parser1("_hi_", {inline: true});
3702
+ validateParse(parsed1, [{content: "hi", type: "em2"}]);
3703
+
3704
+ // ...regardless of their order in the
3705
+ // original rule definition
3706
+
3707
+ // $FlowFixMe
3708
+ var parser2 = SimpleMarkdown.parserFor({
3709
+ em2: Object.assign({}, emRule, {
3710
+ order: 0,
3711
+ quality: function () {
3712
+ return 2;
3713
+ },
3714
+ }),
3715
+ em1: Object.assign({}, emRule, {
3716
+ order: 0,
3717
+ quality: function () {
3718
+ return 1;
3719
+ },
3720
+ }),
3721
+ text: textRule,
3722
+ });
3723
+
3724
+ var parsed2 = parser2("_hi_", {inline: true});
3725
+ validateParse(parsed2, [{content: "hi", type: "em2"}]);
3726
+ });
3727
+
3728
+ it("rules with quality should always win the tie", function () {
3729
+ // $FlowFixMe
3730
+ var parser1 = SimpleMarkdown.parserFor({
3731
+ em1: Object.assign({}, emRule, {
3732
+ order: 0,
3733
+ }),
3734
+ em2: Object.assign({}, emRule, {
3735
+ order: 0,
3736
+ quality: function () {
3737
+ return 2;
3738
+ },
3739
+ }),
3740
+ text: textRule,
3741
+ });
3742
+
3743
+ var parsed1 = parser1("_hi_", {inline: true});
3744
+ validateParse(parsed1, [{content: "hi", type: "em2"}]);
3745
+
3746
+ // except if they don't match
3747
+
3748
+ // $FlowFixMe
3749
+ var parser2 = SimpleMarkdown.parserFor({
3750
+ em: Object.assign({}, emRule, {
3751
+ order: 0,
3752
+ }),
3753
+ strong: Object.assign({}, strongRule, {
3754
+ order: 0,
3755
+ quality: function () {
3756
+ return 2;
3757
+ },
3758
+ }),
3759
+ text: textRule,
3760
+ });
3761
+
3762
+ var parsed2 = parser2("_hi_", {inline: true});
3763
+ validateParse(parsed2, [{content: "hi", type: "em"}]);
3764
+ var parsed2b = parser2("**hi**", {inline: true});
3765
+ validateParse(parsed2b, [{content: "hi", type: "strong"}]);
3766
+ });
3767
+ });
3768
+
3769
+ it("should append arrays returned from `parse` to the AST", function () {
3770
+ var parser1 = SimpleMarkdown.parserFor({
3771
+ fancy: {
3772
+ order: SimpleMarkdown.defaultRules.text.order - 1,
3773
+
3774
+ // $FlowFixMe
3775
+ match: function (/** @type {string} */ source) {
3776
+ return /^.*/.exec(source);
3777
+ },
3778
+ parse: function (
3779
+ /** @type {SimpleMarkdown.Capture} */ capture,
3780
+ /** @type {SimpleMarkdown.Parser} */ parse,
3781
+ /** @type {SimpleMarkdown.State} */ state,
3782
+ ) {
3783
+ return capture[0]
3784
+ .split(" ")
3785
+ .map(function (/** @type {string} */ word) {
3786
+ return {type: "text", content: word};
3787
+ });
3788
+ },
3789
+ },
3790
+ text: SimpleMarkdown.defaultRules.text,
3791
+ });
3792
+
3793
+ var parsed1 = parser1("this is some text", {inline: true});
3794
+ validateParse(parsed1, [
3795
+ {content: "this", type: "text"},
3796
+ {content: "is", type: "text"},
3797
+ {content: "some", type: "text"},
3798
+ {content: "text", type: "text"},
3799
+ ]);
3800
+ });
3801
+
3802
+ it("should support [repeated] data extraction via mutating state", function () {
3803
+ // This is sort of a more complex example than is necessary, but I
3804
+ // wanted to have some more in-depth tests, so here!
3805
+ // This result counts the words in input/output through state, and also
3806
+ // gives a flattened array result of the words.
3807
+ var rules = {
3808
+ Array: {
3809
+ result: function (
3810
+ /** @type {Array<SimpleMarkdown.SingleASTNode>} */ arr,
3811
+ /** @type {SimpleMarkdown.Output<string[]>} */ output,
3812
+ /** @type {SimpleMarkdown.State} */ state,
3813
+ ) {
3814
+ return arr
3815
+ .map(function (node) {
3816
+ return output(node, state);
3817
+ })
3818
+ .filter(function (word) {
3819
+ return !!word;
3820
+ });
3821
+ },
3822
+ },
3823
+ word: {
3824
+ order: SimpleMarkdown.defaultRules.text.order - 1,
3825
+ match: function (/** @type {string} */ source) {
3826
+ return /^\w+/.exec(source);
3827
+ },
3828
+ parse: function (
3829
+ /** @type {SimpleMarkdown.Capture} */ capture,
3830
+ /** @type {SimpleMarkdown.Parser} */ parse,
3831
+ /** @type {SimpleMarkdown.State} */ state,
3832
+ ) {
3833
+ state.wordCount++;
3834
+ return {content: capture[0]};
3835
+ },
3836
+ result: function (
3837
+ /** @type {SimpleMarkdown.SingleASTNode} */ node,
3838
+ /** @type {SimpleMarkdown.NodeOutput<string>} */ output,
3839
+ /** @type {SimpleMarkdown.State} */ state,
3840
+ ) {
3841
+ state.wordCount++;
3842
+ return node.content;
3843
+ },
3844
+ },
3845
+ // $FlowFixMe
3846
+ delimiter: Object.assign({}, SimpleMarkdown.defaultRules.text, {
3847
+ match: function (/** @type {string} */ source) {
3848
+ return /^\W+/.exec(source);
3849
+ },
3850
+ result: function (
3851
+ /** @type {SimpleMarkdown.SingleASTNode} */ node,
3852
+ /** @type {SimpleMarkdown.NodeOutput<string>} */ output,
3853
+ /** @type {SimpleMarkdown.State} */ state,
3854
+ ) {
3855
+ return null;
3856
+ },
3857
+ }),
3858
+ };
3859
+
3860
+ // $FlowFixMe
3861
+ var parse = SimpleMarkdown.parserFor(rules, {wordCount: 0});
3862
+ var output = SimpleMarkdown.outputFor(rules, "result", {
3863
+ wordCount: 0,
3864
+ });
3865
+
3866
+ // test parsing
3867
+ /** @type {{ wordCount?: number }} */
3868
+ var parseState = {};
3869
+ var ast1 = parse("hi here are some words", parseState);
3870
+ assert.strictEqual(parseState.wordCount, 5);
3871
+ // and repeated parsing
3872
+ var ast2 = parse("hi here are some words", parseState);
3873
+ assert.strictEqual(parseState.wordCount, 5);
3874
+ assert.deepEqual(ast1, ast2);
3875
+
3876
+ // test output
3877
+ /** @type {{ wordCount?: number }} */
3878
+ var outputState = {};
3879
+ var result1 = output(ast1, outputState);
3880
+ assert.deepEqual(result1, ["hi", "here", "are", "some", "words"]);
3881
+ assert.strictEqual(outputState.wordCount, 5);
3882
+ var result2 = output(ast2, outputState);
3883
+ assert.strictEqual(outputState.wordCount, 5);
3884
+ assert.deepEqual(result2, ["hi", "here", "are", "some", "words"]);
3885
+ assert.deepEqual(result1, result2);
3886
+ });
3887
+
3888
+ it("should allow default state params in parserFor", function () {
3889
+ var parser1 = SimpleMarkdown.parserFor(
3890
+ // $FlowFixMe
3891
+ {
3892
+ fancy: {
3893
+ order: SimpleMarkdown.defaultRules.text.order - 1,
3894
+ // $FlowFixMe
3895
+ match: function (/** @type {string} */ source) {
3896
+ return /^\w+/.exec(source);
3897
+ },
3898
+ parse: function (
3899
+ /** @type {SimpleMarkdown.Capture} */ capture,
3900
+ /** @type {SimpleMarkdown.Parser} */ parse,
3901
+ /** @type {SimpleMarkdown.State} */ state,
3902
+ ) {
3903
+ var word = capture[0];
3904
+ var translated = state.lookup[word];
3905
+ if (translated) {
3906
+ return {content: translated};
3907
+ } else {
3908
+ return {content: word, type: "text"};
3909
+ }
3910
+ },
3911
+ },
3912
+ // $FlowFixMe
3913
+ text: Object.assign({}, SimpleMarkdown.defaultRules.text, {
3914
+ match: function (/** @type {string} */ source) {
3915
+ return /^\W+/.exec(source);
3916
+ },
3917
+ }),
3918
+ },
3919
+ {
3920
+ lookup: {
3921
+ this: "thís",
3922
+ is: "ìs",
3923
+ text: "têxt",
3924
+ },
3925
+ },
3926
+ );
3927
+
3928
+ var parsed1 = parser1("this is some text", {inline: true});
3929
+ validateParse(parsed1, [
3930
+ {content: "thís", type: "fancy"},
3931
+ {content: " ", type: "text"},
3932
+ {content: "ìs", type: "fancy"},
3933
+ {content: " ", type: "text"},
3934
+ {content: "some", type: "text"},
3935
+ {content: " ", type: "text"},
3936
+ {content: "têxt", type: "fancy"},
3937
+ ]);
3938
+ });
3939
+
3940
+ it("should allow default state params in outputFor", function () {
3941
+ var output = SimpleMarkdown.outputFor(
3942
+ {
3943
+ // $FlowFixMe[incompatible-call]
3944
+ Array: SimpleMarkdown.defaultRules.Array,
3945
+ text: Object.assign({}, SimpleMarkdown.defaultRules.text, {
3946
+ react: function (
3947
+ /** @type {SimpleMarkdown.SingleASTNode} */ node,
3948
+ /** @type {SimpleMarkdown.ReactNodeOutput} */ output,
3949
+ /** @type {SimpleMarkdown.State} */ state,
3950
+ ) {
3951
+ return React.createElement(
3952
+ state.TextComponent,
3953
+ {key: state.key},
3954
+ node.content,
3955
+ );
3956
+ },
3957
+ }),
3958
+ },
3959
+ "react",
3960
+ {
3961
+ // make all text bold
3962
+ TextComponent: "b",
3963
+ },
3964
+ );
3965
+
3966
+ var parsed1 = inlineParse("this is some text");
3967
+ var results1 = output(parsed1);
3968
+
3969
+ assert.strictEqual(
3970
+ reactToHtml(results1),
3971
+ "<b>this is some text</b>",
3972
+ );
3973
+ });
3974
+
3975
+ it("should not require passing state to recursiveParse", function () {
3976
+ var parse = SimpleMarkdown.parserFor(
3977
+ {
3978
+ bracketed: {
3979
+ order: SimpleMarkdown.defaultRules.text.order - 1,
3980
+ // $FlowFixMe
3981
+ match: function (/** @type {string} */ source) {
3982
+ return /^\{((?:\\[\S\s]|[^\\\*])+)\}/.exec(source);
3983
+ },
3984
+ parse: function (
3985
+ /** @type {SimpleMarkdown.Capture} */ capture,
3986
+ /** @type {SimpleMarkdown.Parser} */ parse,
3987
+ /** @type {SimpleMarkdown.State} */ state,
3988
+ ) {
3989
+ var result = {
3990
+ // note no passing state here:
3991
+ content: parse(capture[1]),
3992
+ token: state.token || 0,
3993
+ };
3994
+ state.token = (state.token || 0) + 1;
3995
+ return result;
3996
+ },
3997
+ },
3998
+ text: SimpleMarkdown.defaultRules.text,
3999
+ },
4000
+ {disableAutoBlockNewlines: true},
4001
+ );
4002
+
4003
+ var parsed1 = parse("{outer {inner}}", {inline: true, token: 5327});
4004
+
4005
+ validateParse(parsed1, [
4006
+ {
4007
+ type: "bracketed",
4008
+ content: [
4009
+ {
4010
+ type: "text",
4011
+ content: "outer ",
4012
+ },
4013
+ {
4014
+ type: "bracketed",
4015
+ content: [
4016
+ {
4017
+ type: "text",
4018
+ content: "inner",
4019
+ },
4020
+ ],
4021
+ token: 5327,
4022
+ },
4023
+ ],
4024
+ token: 5328,
4025
+ },
4026
+ ]);
4027
+
4028
+ // but shouldn't keep old state around between parses:
4029
+ var parsed2 = parse("{outer {inner}}");
4030
+
4031
+ validateParse(parsed2, [
4032
+ {
4033
+ type: "bracketed",
4034
+ content: [
4035
+ {
4036
+ type: "text",
4037
+ content: "outer ",
4038
+ },
4039
+ {
4040
+ type: "bracketed",
4041
+ content: [
4042
+ {
4043
+ type: "text",
4044
+ content: "inner",
4045
+ },
4046
+ ],
4047
+ token: 0,
4048
+ },
4049
+ ],
4050
+ token: 1,
4051
+ },
4052
+ ]);
4053
+ });
4054
+
4055
+ it("should not require passing state to recursiveOutput", function () {
4056
+ var output = SimpleMarkdown.outputFor(
4057
+ {
4058
+ // $FlowFixMe[incompatible-call]
4059
+ Array: SimpleMarkdown.defaultRules.Array,
4060
+ paragraph: Object.assign(
4061
+ {},
4062
+ SimpleMarkdown.defaultRules.paragraph,
4063
+ {
4064
+ html: function (
4065
+ /** @type {SimpleMarkdown.SingleASTNode} */ node,
4066
+ /** @type {SimpleMarkdown.HtmlOutput} */ output,
4067
+ ) {
4068
+ return "<p>" + output(node.content) + "</p>";
4069
+ },
4070
+ },
4071
+ ),
4072
+ text: Object.assign({}, SimpleMarkdown.defaultRules.text, {
4073
+ html: function (
4074
+ /** @type {SimpleMarkdown.SingleASTNode} */ node,
4075
+ /** @type {SimpleMarkdown.HtmlOutput} */ output,
4076
+ /** @type {SimpleMarkdown.State} */ state,
4077
+ ) {
4078
+ return (
4079
+ '<span class="' +
4080
+ (state.spanClass || "default") +
4081
+ '">' +
4082
+ /*SimpleMarkdown.sanitizeText*/ node.content +
4083
+ "</span>"
4084
+ );
4085
+ },
4086
+ }),
4087
+ },
4088
+ "html",
4089
+ );
4090
+
4091
+ var parsed1 = SimpleMarkdown.defaultBlockParse("hi there!");
4092
+ var result1 = output(parsed1, {spanClass: "special"});
4093
+ assert.strictEqual(
4094
+ result1,
4095
+ '<p><span class="special">hi there!</span></p>',
4096
+ );
4097
+
4098
+ // but shouldn't keep state around between outputs:
4099
+ var parsed2 = SimpleMarkdown.defaultBlockParse("hi there!");
4100
+ var result2 = output(parsed2);
4101
+ assert.strictEqual(
4102
+ result2,
4103
+ '<p><span class="default">hi there!</span></p>',
4104
+ );
4105
+ });
4106
+
4107
+ it("should ignore null or undefined rules", function () {
4108
+ var rules = {
4109
+ Array: SimpleMarkdown.defaultRules.Array,
4110
+ spoiler: {
4111
+ order: SimpleMarkdown.defaultRules.text.order - 1,
4112
+ match: function (/** @type {string} */ source) {
4113
+ return /^\[\[((?:[^\]]|\][^\]])+)\]\]/.exec(source);
4114
+ },
4115
+ parse: function (
4116
+ /** @type {SimpleMarkdown.Capture} */ capture,
4117
+ /** @type {SimpleMarkdown.Parser} */ parse,
4118
+ ) {
4119
+ return {content: parse(capture[1])};
4120
+ },
4121
+ html: function (
4122
+ /** @type {SimpleMarkdown.SingleASTNode} */ node,
4123
+ /** @type {SimpleMarkdown.HtmlOutput} */ output,
4124
+ ) {
4125
+ return (
4126
+ '<span style="background: black;">' +
4127
+ output(node.content) +
4128
+ "</span>"
4129
+ );
4130
+ },
4131
+ },
4132
+ text: SimpleMarkdown.defaultRules.text,
4133
+ };
4134
+
4135
+ // $FlowFixMe
4136
+ var parse = SimpleMarkdown.parserFor(rules, {inline: true});
4137
+ var output = SimpleMarkdown.outputFor(rules, "html");
4138
+
4139
+ var parsed1 = parse("Hi this is a [[spoiler]]");
4140
+ validateParse(parsed1, [
4141
+ {type: "text", content: "Hi this is a "},
4142
+ {
4143
+ type: "spoiler",
4144
+ content: [{type: "text", content: "spoiler"}],
4145
+ },
4146
+ ]);
4147
+ var result1 = output(parsed1);
4148
+ assert.strictEqual(
4149
+ result1,
4150
+ 'Hi this is a <span style="background: black;">spoiler</span>',
4151
+ );
4152
+ });
4153
+ });
4154
+
4155
+ describe("react output", function () {
4156
+ it("should sanitize dangerous links", function () {
4157
+ var html = htmlFromReactMarkdown(
4158
+ "[link](javascript:alert%28%27hi%27%29)",
4159
+ );
4160
+ assert.strictEqual(html, "<a>link</a>");
4161
+
4162
+ var html2 = htmlFromReactMarkdown(
4163
+ "[link][1]\n\n" + "[1]: javascript:alert('hi');\n\n",
4164
+ );
4165
+ assert.strictEqual(
4166
+ html2,
4167
+ '<div class="paragraph"><a>link</a></div>',
4168
+ );
4169
+
4170
+ var html3 = htmlFromReactMarkdown(
4171
+ "[link](data:text/html;base64,PHNjcmlwdD5hbGVydCgnaGknKTwvc2NyaXB0Pg==)",
4172
+ );
4173
+ assert.strictEqual(html3, "<a>link</a>");
4174
+
4175
+ var html4 = htmlFromReactMarkdown(
4176
+ "[link][1]\n\n" +
4177
+ "[1]: data:text/html;base64,PHNjcmlwdD5hbGVydCgnaGknKTwvc2NyaXB0Pg==\n\n",
4178
+ );
4179
+ assert.strictEqual(
4180
+ html4,
4181
+ '<div class="paragraph"><a>link</a></div>',
4182
+ );
4183
+
4184
+ var html5 = htmlFromReactMarkdown("[link](vbscript:alert)");
4185
+ assert.strictEqual(html5, "<a>link</a>");
4186
+
4187
+ var html6 = htmlFromReactMarkdown(
4188
+ "[link][1]\n\n" + "[1]: vbscript:alert\n\n",
4189
+ );
4190
+ assert.strictEqual(
4191
+ html6,
4192
+ '<div class="paragraph"><a>link</a></div>',
4193
+ );
4194
+ });
4195
+
4196
+ it("should not sanitize safe links", function () {
4197
+ var html = htmlFromReactMarkdown("[link](https://www.google.com)");
4198
+ assert.strictEqual(
4199
+ html,
4200
+ '<a href="https://www.google.com">link</a>',
4201
+ );
4202
+
4203
+ var html2 = htmlFromReactMarkdown(
4204
+ "[link][1]\n\n" + "[1]: https://www.google.com\n\n",
4205
+ );
4206
+ assert.strictEqual(
4207
+ html2,
4208
+ '<div class="paragraph">' +
4209
+ '<a href="https://www.google.com">link</a>' +
4210
+ "</div>",
4211
+ );
4212
+ });
4213
+
4214
+ it("should output headings", function () {
4215
+ assertParsesToReact("### Heading!\n\n", "<h3>Heading!</h3>");
4216
+
4217
+ assertParsesToReact("## hi! ##\n\n", "<h2>hi!</h2>");
4218
+
4219
+ assertParsesToReact("Yay!\n====\n\n", "<h1>Yay!</h1>");
4220
+
4221
+ assertParsesToReact("Success\n---\n\n", "<h2>Success</h2>");
4222
+ });
4223
+
4224
+ it("should output hrs", function () {
4225
+ assertParsesToReact("-----\n\n", "<hr/>");
4226
+ assertParsesToReact(" * * * \n\n", "<hr/>");
4227
+ assertParsesToReact("___\n\n", "<hr/>");
4228
+ });
4229
+
4230
+ it("should output codeblocks", function () {
4231
+ var html = htmlFromReactMarkdown(
4232
+ " var microwave = new TimeMachine();\n\n",
4233
+ );
4234
+ assert.strictEqual(
4235
+ html,
4236
+ "<pre><code>var microwave = new TimeMachine();</code></pre>",
4237
+ );
4238
+
4239
+ var html2 = htmlFromReactMarkdown(
4240
+ "~~~\n" + "var computer = new IBN(5100);\n" + "~~~\n\n",
4241
+ );
4242
+ assert.strictEqual(
4243
+ html2,
4244
+ "<pre><code>var computer = new IBN(5100);</code></pre>",
4245
+ );
4246
+
4247
+ var html3 = htmlFromReactMarkdown(
4248
+ "```yavascript\n" +
4249
+ "var undefined = function() { return 5; }" +
4250
+ "```\n\n",
4251
+ );
4252
+ assert.strictEqual(
4253
+ html3,
4254
+ '<pre><code class="markdown-code-yavascript">' +
4255
+ "var undefined = function() { return 5; }" +
4256
+ "</code></pre>",
4257
+ );
4258
+ });
4259
+
4260
+ it("should output blockQuotes", function () {
4261
+ assertParsesToReact(
4262
+ "> hi there this is a\ntest\n\n",
4263
+ '<blockquote><div class="paragraph">' +
4264
+ "hi there this is a test" +
4265
+ "</div></blockquote>",
4266
+ );
4267
+
4268
+ assertParsesToReact(
4269
+ "> hi there this is a\n> test\n\n",
4270
+ '<blockquote><div class="paragraph">' +
4271
+ "hi there this is a test" +
4272
+ "</div></blockquote>",
4273
+ );
4274
+ });
4275
+
4276
+ it("should output lists", function () {
4277
+ assertParsesToReact(
4278
+ " * first\n" + " * second\n" + " * third\n\n",
4279
+ "<ul>" +
4280
+ "<li>first</li>" +
4281
+ "<li>second</li>" +
4282
+ "<li>third</li>" +
4283
+ "</ul>",
4284
+ );
4285
+
4286
+ assertParsesToReact(
4287
+ "1. first\n" + "2. second\n" + "3. third\n\n",
4288
+ '<ol start="1">' +
4289
+ "<li>first</li>" +
4290
+ "<li>second</li>" +
4291
+ "<li>third</li>" +
4292
+ "</ol>",
4293
+ );
4294
+
4295
+ assertParsesToReact(
4296
+ " * first\n" + " * second\n" + " * inner\n" + " * third\n\n",
4297
+ "<ul>" +
4298
+ "<li>first</li>" +
4299
+ "<li>second <ul><li>inner</li></ul></li>" +
4300
+ "<li>third</li>" +
4301
+ "</ul>",
4302
+ );
4303
+ });
4304
+
4305
+ it("should output tables", function () {
4306
+ assertParsesToReact(
4307
+ "h1 | h2 | h3\n" + "---|----|---\n" + "d1 | d2 | d3\n" + "\n",
4308
+ "<table><thead>" +
4309
+ '<tr><th scope="col">h1</th><th scope="col">h2</th><th scope="col">h3</th></tr>' +
4310
+ "</thead><tbody>" +
4311
+ "<tr><td>d1</td><td>d2</td><td>d3</td></tr>" +
4312
+ "</tbody></table>",
4313
+ );
4314
+
4315
+ assertParsesToReact(
4316
+ "| h1 | h2 | h3 |\n" +
4317
+ "|----|----|----|\n" +
4318
+ "| d1 | d2 | d3 |\n" +
4319
+ "\n",
4320
+ "<table><thead>" +
4321
+ '<tr><th scope="col">h1</th><th scope="col">h2</th><th scope="col">h3</th></tr>' +
4322
+ "</thead><tbody>" +
4323
+ "<tr><td>d1</td><td>d2</td><td>d3</td></tr>" +
4324
+ "</tbody></table>",
4325
+ );
4326
+
4327
+ assertParsesToReact(
4328
+ "h1 | h2 | h3\n" + ":--|:--:|--:\n" + "d1 | d2 | d3\n" + "\n",
4329
+ "<table><thead>" +
4330
+ "<tr>" +
4331
+ '<th style="text-align:left" scope="col">h1</th>' +
4332
+ '<th style="text-align:center" scope="col">h2</th>' +
4333
+ '<th style="text-align:right" scope="col">h3</th>' +
4334
+ "</tr>" +
4335
+ "</thead><tbody>" +
4336
+ "<tr>" +
4337
+ '<td style="text-align:left">d1</td>' +
4338
+ '<td style="text-align:center">d2</td>' +
4339
+ '<td style="text-align:right">d3</td>' +
4340
+ "</tr>" +
4341
+ "</tbody></table>",
4342
+ );
4343
+ });
4344
+
4345
+ // TODO(aria): Figure out how to test the newline rule here
4346
+
4347
+ it("should output paragraphs", function () {
4348
+ var html = htmlFromReactMarkdown("hi\n\n");
4349
+ assert.strictEqual(html, '<div class="paragraph">hi</div>');
4350
+
4351
+ var html2 = htmlFromReactMarkdown("hi\n\n" + "bye\n\n");
4352
+ assert.strictEqual(
4353
+ html2,
4354
+ '<div class="paragraph">hi</div>' +
4355
+ '<div class="paragraph">bye</div>',
4356
+ );
4357
+ });
4358
+
4359
+ it("should output escaped text", function () {
4360
+ assertParsesToReact(
4361
+ "\\#escaping\\^symbols\\*is\\[legal](yes)",
4362
+ "#escaping^symbols*is[legal](yes)",
4363
+ );
4364
+ });
4365
+
4366
+ it("should output links", function () {
4367
+ assertParsesToReact(
4368
+ "<https://www.khanacademy.org>",
4369
+ '<a href="https://www.khanacademy.org">' +
4370
+ "https://www.khanacademy.org" +
4371
+ "</a>",
4372
+ );
4373
+
4374
+ assertParsesToReact(
4375
+ "<aria@khanacademy.org>",
4376
+ '<a href="mailto:aria@khanacademy.org">' +
4377
+ "aria@khanacademy.org" +
4378
+ "</a>",
4379
+ );
4380
+
4381
+ assertParsesToReact(
4382
+ "https://www.khanacademy.org",
4383
+ '<a href="https://www.khanacademy.org">' +
4384
+ "https://www.khanacademy.org" +
4385
+ "</a>",
4386
+ );
4387
+
4388
+ assertParsesToReact(
4389
+ "[KA](https://www.khanacademy.org)",
4390
+ '<a href="https://www.khanacademy.org">' + "KA" + "</a>",
4391
+ );
4392
+
4393
+ assertParsesToReact(
4394
+ "[KA][1]\n\n[1]: https://www.khanacademy.org\n\n",
4395
+ '<div class="paragraph">' +
4396
+ '<a href="https://www.khanacademy.org">' +
4397
+ "KA" +
4398
+ "</a>" +
4399
+ "</div>",
4400
+ );
4401
+ });
4402
+
4403
+ it("should output strong", function () {
4404
+ assertParsesToReact("**bold**", "<strong>bold</strong>");
4405
+ });
4406
+
4407
+ it("should output u", function () {
4408
+ assertParsesToReact("__underscore__", "<u>underscore</u>");
4409
+ });
4410
+
4411
+ it("should output em", function () {
4412
+ assertParsesToReact("*italics*", "<em>italics</em>");
4413
+ });
4414
+
4415
+ it("should output simple combined bold/italics", function () {
4416
+ assertParsesToReact(
4417
+ "***bolditalics***",
4418
+ "<em><strong>bolditalics</strong></em>",
4419
+ );
4420
+ assertParsesToReact(
4421
+ "**bold *italics***",
4422
+ "<strong>bold <em>italics</em></strong>",
4423
+ );
4424
+ });
4425
+
4426
+ it("should output complex combined bold/italics", function () {
4427
+ assertParsesToReact(
4428
+ "***bold** italics*",
4429
+ "<em><strong>bold</strong> italics</em>",
4430
+ );
4431
+ assertParsesToReact(
4432
+ "*hi **there you***",
4433
+ "<em>hi <strong>there you</strong></em>",
4434
+ );
4435
+ });
4436
+
4437
+ it("should output del", function () {
4438
+ assertParsesToReact(
4439
+ "~~strikethrough~~",
4440
+ "<del>strikethrough</del>",
4441
+ );
4442
+ });
4443
+
4444
+ it("should output inline code", function () {
4445
+ assertParsesToReact(
4446
+ "here is some `inline code`.",
4447
+ "here is some <code>inline code</code>.",
4448
+ );
4449
+ });
4450
+
4451
+ it("should output text", function () {
4452
+ assertParsesToReact("Yay text!", "Yay text!");
4453
+ });
4454
+
4455
+ it("shouldn't split text into multiple spans", function () {
4456
+ var parsed = SimpleMarkdown.defaultInlineParse("hi, there!");
4457
+ var elements = SimpleMarkdown.defaultReactOutput(parsed);
4458
+ assert.deepEqual(elements, ["hi, there!"]);
4459
+ });
4460
+
4461
+ it("should join text nodes before outputting them", function () {
4462
+ var rules = Object.assign({}, SimpleMarkdown.defaultRules, {
4463
+ text: Object.assign({}, SimpleMarkdown.defaultRules.text, {
4464
+ react: function (
4465
+ /** @type {SimpleMarkdown.SingleASTNode} */ node,
4466
+ /** @type {SimpleMarkdown.ReactOutput} */ output,
4467
+ /** @type {SimpleMarkdown.State} */ state,
4468
+ ) {
4469
+ return React.createElement(
4470
+ "span",
4471
+ {key: state.key, className: "text"},
4472
+ node.content,
4473
+ );
4474
+ },
4475
+ }),
4476
+ });
4477
+
4478
+ // $FlowFixMe[incompatible-call]
4479
+ var output = SimpleMarkdown.outputFor(rules, "react");
4480
+
4481
+ var parsed = SimpleMarkdown.defaultInlineParse("Hi! You! Are! <3!");
4482
+
4483
+ var html = reactToHtml(output(parsed));
4484
+
4485
+ assert.strictEqual(
4486
+ html,
4487
+ '<span class="text">Hi! You! Are! &lt;3!</span>',
4488
+ );
4489
+ });
4490
+ });
4491
+
4492
+ describe("html output", function () {
4493
+ it("should sanitize dangerous links", function () {
4494
+ var markdown = "[link](javascript:alert%28%27hi%27%29)";
4495
+ assertParsesToHtml(markdown, "<a>link</a>");
4496
+
4497
+ var markdown2 =
4498
+ "[link][1]\n\n" + "[1]: javascript:alert('hi');\n\n";
4499
+ assertParsesToHtml(
4500
+ markdown2,
4501
+ '<div class="paragraph"><a>link</a></div>',
4502
+ );
4503
+
4504
+ var markdown3 =
4505
+ "[link](data:text/html;base64,PHNjcmlwdD5hbGVydCgnaGknKTwvc2NyaXB0Pg==)";
4506
+ assertParsesToHtml(markdown3, "<a>link</a>");
4507
+
4508
+ var markdown4 =
4509
+ "[link][1]\n\n" +
4510
+ "[1]: data:text/html;base64,PHNjcmlwdD5hbGVydCgnaGknKTwvc2NyaXB0Pg==\n\n";
4511
+ assertParsesToHtml(
4512
+ markdown4,
4513
+ '<div class="paragraph"><a>link</a></div>',
4514
+ );
4515
+
4516
+ var markdown5 = "[link](vbscript:alert)";
4517
+ assertParsesToHtml(markdown5, "<a>link</a>");
4518
+
4519
+ var markdown6 = "[link][1]\n\n" + "[1]: vbscript:alert\n\n";
4520
+ assertParsesToHtml(
4521
+ markdown6,
4522
+ '<div class="paragraph"><a>link</a></div>',
4523
+ );
4524
+ });
4525
+
4526
+ it("should not sanitize safe links", function () {
4527
+ var html = htmlFromMarkdown("[link](https://www.google.com)");
4528
+ assert.strictEqual(
4529
+ html,
4530
+ '<a href="https://www.google.com">link</a>',
4531
+ );
4532
+
4533
+ var html2 = htmlFromMarkdown(
4534
+ "[link][1]\n\n" + "[1]: https://www.google.com\n\n",
4535
+ );
4536
+ assert.strictEqual(
4537
+ html2,
4538
+ '<div class="paragraph">' +
4539
+ '<a href="https://www.google.com">link</a>' +
4540
+ "</div>",
4541
+ );
4542
+ });
4543
+
4544
+ it("should output headings", function () {
4545
+ assertParsesToHtml("### Heading!\n\n", "<h3>Heading!</h3>");
4546
+
4547
+ assertParsesToHtml("## hi! ##\n\n", "<h2>hi!</h2>");
4548
+
4549
+ assertParsesToHtml("Yay!\n====\n\n", "<h1>Yay!</h1>");
4550
+
4551
+ assertParsesToHtml("Success\n---\n\n", "<h2>Success</h2>");
4552
+ });
4553
+
4554
+ it("should output hrs", function () {
4555
+ assertParsesToHtml("-----\n\n", "<hr>");
4556
+ assertParsesToHtml(" * * * \n\n", "<hr>");
4557
+ assertParsesToHtml("___\n\n", "<hr>");
4558
+ });
4559
+
4560
+ it("should output codeblocks", function () {
4561
+ var html = htmlFromMarkdown(
4562
+ " var microwave = new TimeMachine();\n\n",
4563
+ );
4564
+ assert.strictEqual(
4565
+ html,
4566
+ "<pre><code>var microwave = new TimeMachine();</code></pre>",
4567
+ );
4568
+
4569
+ var html2 = htmlFromMarkdown(
4570
+ "~~~\n" + "var computer = new IBN(5100);\n" + "~~~\n\n",
4571
+ );
4572
+ assert.strictEqual(
4573
+ html2,
4574
+ "<pre><code>var computer = new IBN(5100);</code></pre>",
4575
+ );
4576
+
4577
+ var html3 = htmlFromMarkdown(
4578
+ "```yavascript\n" +
4579
+ "var undefined = function() { return 5; }" +
4580
+ "```\n\n",
4581
+ );
4582
+ assert.strictEqual(
4583
+ html3,
4584
+ '<pre><code class="markdown-code-yavascript">' +
4585
+ "var undefined = function() { return 5; }" +
4586
+ "</code></pre>",
4587
+ );
4588
+ });
4589
+
4590
+ it("should output blockQuotes", function () {
4591
+ assertParsesToHtml(
4592
+ "> hi there this is a\ntest\n\n",
4593
+ '<blockquote><div class="paragraph">' +
4594
+ "hi there this is a test" +
4595
+ "</div></blockquote>",
4596
+ );
4597
+
4598
+ assertParsesToHtml(
4599
+ "> hi there this is a\n> test\n\n",
4600
+ '<blockquote><div class="paragraph">' +
4601
+ "hi there this is a test" +
4602
+ "</div></blockquote>",
4603
+ );
4604
+ });
4605
+
4606
+ it("should output lists", function () {
4607
+ assertParsesToHtml(
4608
+ " * first\n" + " * second\n" + " * third\n\n",
4609
+ "<ul>" +
4610
+ "<li>first</li>" +
4611
+ "<li>second</li>" +
4612
+ "<li>third</li>" +
4613
+ "</ul>",
4614
+ );
4615
+
4616
+ assertParsesToHtml(
4617
+ "1. first\n" + "2. second\n" + "3. third\n\n",
4618
+ '<ol start="1">' +
4619
+ "<li>first</li>" +
4620
+ "<li>second</li>" +
4621
+ "<li>third</li>" +
4622
+ "</ol>",
4623
+ );
4624
+
4625
+ assertParsesToHtml(
4626
+ " * first\n" + " * second\n" + " * inner\n" + " * third\n\n",
4627
+ "<ul>" +
4628
+ "<li>first</li>" +
4629
+ "<li>second <ul><li>inner</li></ul></li>" +
4630
+ "<li>third</li>" +
4631
+ "</ul>",
4632
+ );
4633
+ });
4634
+
4635
+ it("should output tables", function () {
4636
+ assertParsesToHtml(
4637
+ "h1 | h2 | h3\n" + "---|----|---\n" + "d1 | d2 | d3\n" + "\n",
4638
+ "<table><thead>" +
4639
+ '<tr><th scope="col">h1</th><th scope="col">h2</th><th scope="col">h3</th></tr>' +
4640
+ "</thead><tbody>" +
4641
+ "<tr><td>d1</td><td>d2</td><td>d3</td></tr>" +
4642
+ "</tbody></table>",
4643
+ );
4644
+
4645
+ assertParsesToHtml(
4646
+ "| h1 | h2 | h3 |\n" +
4647
+ "|----|----|----|\n" +
4648
+ "| d1 | d2 | d3 |\n" +
4649
+ "\n",
4650
+ "<table><thead>" +
4651
+ '<tr><th scope="col">h1</th><th scope="col">h2</th><th scope="col">h3</th></tr>' +
4652
+ "</thead><tbody>" +
4653
+ "<tr><td>d1</td><td>d2</td><td>d3</td></tr>" +
4654
+ "</tbody></table>",
4655
+ );
4656
+
4657
+ assertParsesToHtml(
4658
+ "h1 | h2 | h3\n" + ":--|:--:|--:\n" + "d1 | d2 | d3\n" + "\n",
4659
+ "<table><thead>" +
4660
+ "<tr>" +
4661
+ '<th style="text-align:left;" scope="col">h1</th>' +
4662
+ '<th style="text-align:center;" scope="col">h2</th>' +
4663
+ '<th style="text-align:right;" scope="col">h3</th>' +
4664
+ "</tr>" +
4665
+ "</thead><tbody>" +
4666
+ "<tr>" +
4667
+ '<td style="text-align:left;">d1</td>' +
4668
+ '<td style="text-align:center;">d2</td>' +
4669
+ '<td style="text-align:right;">d3</td>' +
4670
+ "</tr>" +
4671
+ "</tbody></table>",
4672
+ );
4673
+ });
4674
+
4675
+ // TODO(aria): Figure out how to test the newline rule here
4676
+
4677
+ it("should output paragraphs", function () {
4678
+ var html = htmlFromMarkdown("hi\n\n");
4679
+ assert.strictEqual(html, '<div class="paragraph">hi</div>');
4680
+
4681
+ var html2 = htmlFromMarkdown("hi\n\n" + "bye\n\n");
4682
+ assert.strictEqual(
4683
+ html2,
4684
+ '<div class="paragraph">hi</div>' +
4685
+ '<div class="paragraph">bye</div>',
4686
+ );
4687
+ });
4688
+
4689
+ it("should output escaped text", function () {
4690
+ assertParsesToHtml(
4691
+ "\\#escaping\\^symbols\\*is\\[legal](yes)",
4692
+ "#escaping^symbols*is[legal](yes)",
4693
+ );
4694
+ });
4695
+
4696
+ it("should output links", function () {
4697
+ assertParsesToHtml(
4698
+ "<https://www.khanacademy.org>",
4699
+ '<a href="https://www.khanacademy.org">' +
4700
+ "https://www.khanacademy.org" +
4701
+ "</a>",
4702
+ );
4703
+
4704
+ assertParsesToHtml(
4705
+ "<aria@khanacademy.org>",
4706
+ '<a href="mailto:aria@khanacademy.org">' +
4707
+ "aria@khanacademy.org" +
4708
+ "</a>",
4709
+ );
4710
+
4711
+ assertParsesToHtml(
4712
+ "https://www.khanacademy.org",
4713
+ '<a href="https://www.khanacademy.org">' +
4714
+ "https://www.khanacademy.org" +
4715
+ "</a>",
4716
+ );
4717
+
4718
+ assertParsesToHtml(
4719
+ "[KA](https://www.khanacademy.org)",
4720
+ '<a href="https://www.khanacademy.org">' + "KA" + "</a>",
4721
+ );
4722
+
4723
+ assertParsesToHtml(
4724
+ "[KA][1]\n\n[1]: https://www.khanacademy.org\n\n",
4725
+ '<div class="paragraph">' +
4726
+ '<a href="https://www.khanacademy.org">' +
4727
+ "KA" +
4728
+ "</a>" +
4729
+ "</div>",
4730
+ );
4731
+ });
4732
+
4733
+ it("should output strong", function () {
4734
+ assertParsesToHtml("**bold**", "<strong>bold</strong>");
4735
+ });
4736
+
4737
+ it("should output u", function () {
4738
+ assertParsesToHtml("__underscore__", "<u>underscore</u>");
4739
+ });
4740
+
4741
+ it("should output em", function () {
4742
+ assertParsesToHtml("*italics*", "<em>italics</em>");
4743
+ });
4744
+
4745
+ it("should output simple combined bold/italics", function () {
4746
+ assertParsesToHtml(
4747
+ "***bolditalics***",
4748
+ "<em><strong>bolditalics</strong></em>",
4749
+ );
4750
+ assertParsesToHtml(
4751
+ "**bold *italics***",
4752
+ "<strong>bold <em>italics</em></strong>",
4753
+ );
4754
+ });
4755
+
4756
+ it("should output complex combined bold/italics", function () {
4757
+ assertParsesToHtml(
4758
+ "***bold** italics*",
4759
+ "<em><strong>bold</strong> italics</em>",
4760
+ );
4761
+ assertParsesToHtml(
4762
+ "*hi **there you***",
4763
+ "<em>hi <strong>there you</strong></em>",
4764
+ );
4765
+ });
4766
+
4767
+ it("should output del", function () {
4768
+ assertParsesToHtml("~~strikethrough~~", "<del>strikethrough</del>");
4769
+ });
4770
+
4771
+ it("should output inline code", function () {
4772
+ assertParsesToHtml(
4773
+ "here is some `inline code`.",
4774
+ "here is some <code>inline code</code>.",
4775
+ );
4776
+ });
4777
+
4778
+ it("should output text", function () {
4779
+ assertParsesToHtml("Yay text!", "Yay text!");
4780
+ });
4781
+
4782
+ it("shouldn't split text into multiple spans", function () {
4783
+ var parsed = SimpleMarkdown.defaultInlineParse("hi, there!");
4784
+ var elements = SimpleMarkdown.defaultHtmlOutput(parsed);
4785
+ assert.deepEqual(elements, "hi, there!");
4786
+ });
4787
+ });
4788
+
4789
+ describe("convenience wrappers", function () {
4790
+ describe("markdownToReact", function () {
4791
+ it("should work on a basic 2 paragraph input", function () {
4792
+ var elems = SimpleMarkdown.markdownToReact("Hi there!\n\nYay!");
4793
+
4794
+ assert.strictEqual(
4795
+ reactToHtml(elems),
4796
+ '<div class="paragraph">Hi there!</div>' +
4797
+ '<div class="paragraph">Yay!</div>',
4798
+ );
4799
+ });
4800
+ });
4801
+
4802
+ describe("markdownToHtml", function () {
4803
+ it("should work on a basic 2 paragraph input", function () {
4804
+ var html = SimpleMarkdown.markdownToHtml("Hi there!\n\nYay!");
4805
+
4806
+ assert.strictEqual(
4807
+ html,
4808
+ '<div class="paragraph">Hi there!</div>' +
4809
+ '<div class="paragraph">Yay!</div>',
4810
+ );
4811
+ });
4812
+ });
4813
+
4814
+ describe("ReactMarkdown component", function () {
4815
+ it("should work on a basic 2 paragraph input", function () {
4816
+ var elem = React.createElement(SimpleMarkdown.ReactMarkdown, {
4817
+ source: "Hi there!\n\nYay!",
4818
+ });
4819
+
4820
+ assert.strictEqual(
4821
+ reactToHtml(elem),
4822
+ "<div>" +
4823
+ '<div class="paragraph">Hi there!</div>' +
4824
+ '<div class="paragraph">Yay!</div>' +
4825
+ "</div>",
4826
+ );
4827
+ });
4828
+ });
4829
+ });
4830
+
4831
+ describe("helper functions", function () {
4832
+ describe("sanitizeText", function () {
4833
+ it("should escape basic html", function () {
4834
+ var result = SimpleMarkdown.sanitizeText(
4835
+ 'hi <span class="my-class">there</span>',
4836
+ );
4837
+ assert.strictEqual(
4838
+ result,
4839
+ "hi &lt;span class=&quot;my-class&quot;&gt;there&lt;/span&gt;",
4840
+ );
4841
+ });
4842
+ });
4843
+ });
4844
+
4845
+ describe("Exponential backtracking avoidance", function () {
4846
+ it("should parse long inlineCode quickly", function () {
4847
+ var source = "`" + Array(2000).join(" ");
4848
+ var startTime = Date.now();
4849
+ var parsed = inlineParse(source);
4850
+ var duration = Date.now() - startTime;
4851
+ assert.ok(
4852
+ duration < 10,
4853
+ "Expected parsing to finish in <10ms, but was " +
4854
+ duration +
4855
+ "ms.",
4856
+ );
4857
+ });
4858
+
4859
+ it("should parse long headings quickly", function () {
4860
+ var source = "### Hi " + Array(1200).join(" ") + "there ### \n\n";
4861
+ var startTime = Date.now();
4862
+ var parsed = blockParse(source);
4863
+ var duration = Date.now() - startTime;
4864
+ assert.ok(
4865
+ duration < 10,
4866
+ "Expected parsing to finish in <10ms, but was " +
4867
+ duration +
4868
+ "ms.",
4869
+ );
4870
+ });
4871
+
4872
+ it("should parse long del strikethroughs quickly", function () {
4873
+ var source = "~~~h" + Array(30).join(" ") + "i";
4874
+ var startTime = Date.now();
4875
+ var parsed = inlineParse(source);
4876
+ var duration = Date.now() - startTime;
4877
+ assert.ok(
4878
+ duration < 10,
4879
+ "Expected parsing to finish in <10ms, but was " +
4880
+ duration +
4881
+ "ms.",
4882
+ );
4883
+
4884
+ source = "~~~h" + Array(1000).join(" ") + "i";
4885
+ startTime = Date.now();
4886
+ parsed = inlineParse(source);
4887
+ duration = Date.now() - startTime;
4888
+ assert.ok(
4889
+ duration < 10,
4890
+ "Expected parsing to finish in <10ms, but was " +
4891
+ duration +
4892
+ "ms.",
4893
+ );
4894
+ });
4895
+
4896
+ it("should parse long code fences quickly", function () {
4897
+ var source =
4898
+ "~~~" +
4899
+ Array(1000).join(" ") +
4900
+ "\n" +
4901
+ Array(1000).join(" ") +
4902
+ "\n";
4903
+ var startTime = Date.now();
4904
+ var parsed = blockParse(source);
4905
+ var duration = Date.now() - startTime;
4906
+ assert.ok(
4907
+ duration < 10,
4908
+ "Expected parsing to finish in <10ms, but was " +
4909
+ duration +
4910
+ "ms.",
4911
+ );
4912
+
4913
+ var source =
4914
+ "~~~" +
4915
+ Array(2000).join(" ") +
4916
+ "\n" +
4917
+ Array(10000).join(" ") +
4918
+ "\n";
4919
+ var startTime = Date.now();
4920
+ var parsed = blockParse(source);
4921
+ var duration = Date.now() - startTime;
4922
+ assert.ok(
4923
+ duration < 10,
4924
+ "Expected parsing to finish in <10ms, but was " +
4925
+ duration +
4926
+ "ms.",
4927
+ );
4928
+ });
4929
+
4930
+ it("should parse long strikethroughs with lots of backslasher quickly", function () {
4931
+ var source =
4932
+ "~~\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}\\}" +
4933
+ "\\}\\}\\}\\}\\}\\}\\}\\}\\\\}\\}\\}\\}\\}\\}\\}}\\}\\}\\}\\}\\}\\}}~";
4934
+
4935
+ var startTime = Date.now();
4936
+ var parsed = blockParse(source);
4937
+ var duration = Date.now() - startTime;
4938
+ assert.ok(
4939
+ duration < 10,
4940
+ "Expected parsing to finish in <10ms, but was " +
4941
+ duration +
4942
+ "ms.",
4943
+ );
4944
+ });
4945
+ });
4946
+ });