marked 7.0.1 → 7.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Tokenizer.ts DELETED
@@ -1,810 +0,0 @@
1
- import { _defaults } from './defaults.ts';
2
- import {
3
- rtrim,
4
- splitCells,
5
- escape,
6
- findClosingBracket
7
- } from './helpers.ts';
8
- import { _Lexer } from './Lexer.ts';
9
- import type { Links, Tokens } from './Tokens.ts';
10
- import type { MarkedOptions } from './MarkedOptions.ts';
11
-
12
- function outputLink(cap: string[], link: Pick<Tokens.Link, 'href' | 'title'>, raw: string, lexer: _Lexer): Tokens.Link | Tokens.Image {
13
- const href = link.href;
14
- const title = link.title ? escape(link.title) : null;
15
- const text = cap[1].replace(/\\([\[\]])/g, '$1');
16
-
17
- if (cap[0].charAt(0) !== '!') {
18
- lexer.state.inLink = true;
19
- const token: Tokens.Link = {
20
- type: 'link',
21
- raw,
22
- href,
23
- title,
24
- text,
25
- tokens: lexer.inlineTokens(text)
26
- };
27
- lexer.state.inLink = false;
28
- return token;
29
- }
30
- return {
31
- type: 'image',
32
- raw,
33
- href,
34
- title,
35
- text: escape(text)
36
- };
37
- }
38
-
39
- function indentCodeCompensation(raw: string, text: string) {
40
- const matchIndentToCode = raw.match(/^(\s+)(?:```)/);
41
-
42
- if (matchIndentToCode === null) {
43
- return text;
44
- }
45
-
46
- const indentToCode = matchIndentToCode[1];
47
-
48
- return text
49
- .split('\n')
50
- .map(node => {
51
- const matchIndentInNode = node.match(/^\s+/);
52
- if (matchIndentInNode === null) {
53
- return node;
54
- }
55
-
56
- const [indentInNode] = matchIndentInNode;
57
-
58
- if (indentInNode.length >= indentToCode.length) {
59
- return node.slice(indentToCode.length);
60
- }
61
-
62
- return node;
63
- })
64
- .join('\n');
65
- }
66
-
67
- /**
68
- * Tokenizer
69
- */
70
- export class _Tokenizer {
71
- options: MarkedOptions;
72
- rules: any;
73
- lexer!: _Lexer;
74
-
75
- constructor(options?: MarkedOptions) {
76
- this.options = options || _defaults;
77
- }
78
-
79
- space(src: string): Tokens.Space | undefined {
80
- const cap = this.rules.block.newline.exec(src);
81
- if (cap && cap[0].length > 0) {
82
- return {
83
- type: 'space',
84
- raw: cap[0]
85
- };
86
- }
87
- }
88
-
89
- code(src: string): Tokens.Code | undefined {
90
- const cap = this.rules.block.code.exec(src);
91
- if (cap) {
92
- const text = cap[0].replace(/^ {1,4}/gm, '');
93
- return {
94
- type: 'code',
95
- raw: cap[0],
96
- codeBlockStyle: 'indented',
97
- text: !this.options.pedantic
98
- ? rtrim(text, '\n')
99
- : text
100
- };
101
- }
102
- }
103
-
104
- fences(src: string): Tokens.Code | undefined {
105
- const cap = this.rules.block.fences.exec(src);
106
- if (cap) {
107
- const raw = cap[0];
108
- const text = indentCodeCompensation(raw, cap[3] || '');
109
-
110
- return {
111
- type: 'code',
112
- raw,
113
- lang: cap[2] ? cap[2].trim().replace(this.rules.inline._escapes, '$1') : cap[2],
114
- text
115
- };
116
- }
117
- }
118
-
119
- heading(src: string): Tokens.Heading | undefined {
120
- const cap = this.rules.block.heading.exec(src);
121
- if (cap) {
122
- let text = cap[2].trim();
123
-
124
- // remove trailing #s
125
- if (/#$/.test(text)) {
126
- const trimmed = rtrim(text, '#');
127
- if (this.options.pedantic) {
128
- text = trimmed.trim();
129
- } else if (!trimmed || / $/.test(trimmed)) {
130
- // CommonMark requires space before trailing #s
131
- text = trimmed.trim();
132
- }
133
- }
134
-
135
- return {
136
- type: 'heading',
137
- raw: cap[0],
138
- depth: cap[1].length,
139
- text,
140
- tokens: this.lexer.inline(text)
141
- };
142
- }
143
- }
144
-
145
- hr(src: string): Tokens.Hr | undefined {
146
- const cap = this.rules.block.hr.exec(src);
147
- if (cap) {
148
- return {
149
- type: 'hr',
150
- raw: cap[0]
151
- };
152
- }
153
- }
154
-
155
- blockquote(src: string): Tokens.Blockquote | undefined {
156
- const cap = this.rules.block.blockquote.exec(src);
157
- if (cap) {
158
- const text = cap[0].replace(/^ *>[ \t]?/gm, '');
159
- const top = this.lexer.state.top;
160
- this.lexer.state.top = true;
161
- const tokens = this.lexer.blockTokens(text);
162
- this.lexer.state.top = top;
163
- return {
164
- type: 'blockquote',
165
- raw: cap[0],
166
- tokens,
167
- text
168
- };
169
- }
170
- }
171
-
172
- list(src: string): Tokens.List | undefined {
173
- let cap = this.rules.block.list.exec(src);
174
- if (cap) {
175
- let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine,
176
- line, nextLine, rawLine, itemContents, endEarly;
177
-
178
- let bull = cap[1].trim();
179
- const isordered = bull.length > 1;
180
-
181
- const list: Tokens.List = {
182
- type: 'list',
183
- raw: '',
184
- ordered: isordered,
185
- start: isordered ? +bull.slice(0, -1) : '',
186
- loose: false,
187
- items: [] as Tokens.ListItem[]
188
- };
189
-
190
- bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
191
-
192
- if (this.options.pedantic) {
193
- bull = isordered ? bull : '[*+-]';
194
- }
195
-
196
- // Get next list item
197
- const itemRegex = new RegExp(`^( {0,3}${bull})((?:[\t ][^\\n]*)?(?:\\n|$))`);
198
-
199
- // Check if current bullet point can start a new List Item
200
- while (src) {
201
- endEarly = false;
202
- if (!(cap = itemRegex.exec(src))) {
203
- break;
204
- }
205
-
206
- if (this.rules.block.hr.test(src)) { // End list if bullet was actually HR (possibly move into itemRegex?)
207
- break;
208
- }
209
-
210
- raw = cap[0];
211
- src = src.substring(raw.length);
212
-
213
- line = cap[2].split('\n', 1)[0].replace(/^\t+/, (t: string) => ' '.repeat(3 * t.length));
214
- nextLine = src.split('\n', 1)[0];
215
-
216
- if (this.options.pedantic) {
217
- indent = 2;
218
- itemContents = line.trimLeft();
219
- } else {
220
- indent = cap[2].search(/[^ ]/); // Find first non-space char
221
- indent = indent > 4 ? 1 : indent; // Treat indented code blocks (> 4 spaces) as having only 1 indent
222
- itemContents = line.slice(indent);
223
- indent += cap[1].length;
224
- }
225
-
226
- blankLine = false;
227
-
228
- if (!line && /^ *$/.test(nextLine)) { // Items begin with at most one blank line
229
- raw += nextLine + '\n';
230
- src = src.substring(nextLine.length + 1);
231
- endEarly = true;
232
- }
233
-
234
- if (!endEarly) {
235
- const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`);
236
- const hrRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`);
237
- const fencesBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:\`\`\`|~~~)`);
238
- const headingBeginRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}#`);
239
-
240
- // Check if following lines should be included in List Item
241
- while (src) {
242
- rawLine = src.split('\n', 1)[0];
243
- nextLine = rawLine;
244
-
245
- // Re-align to follow commonmark nesting rules
246
- if (this.options.pedantic) {
247
- nextLine = nextLine.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');
248
- }
249
-
250
- // End list item if found code fences
251
- if (fencesBeginRegex.test(nextLine)) {
252
- break;
253
- }
254
-
255
- // End list item if found start of new heading
256
- if (headingBeginRegex.test(nextLine)) {
257
- break;
258
- }
259
-
260
- // End list item if found start of new bullet
261
- if (nextBulletRegex.test(nextLine)) {
262
- break;
263
- }
264
-
265
- // Horizontal rule found
266
- if (hrRegex.test(src)) {
267
- break;
268
- }
269
-
270
- if (nextLine.search(/[^ ]/) >= indent || !nextLine.trim()) { // Dedent if possible
271
- itemContents += '\n' + nextLine.slice(indent);
272
- } else {
273
- // not enough indentation
274
- if (blankLine) {
275
- break;
276
- }
277
-
278
- // paragraph continuation unless last line was a different block level element
279
- if (line.search(/[^ ]/) >= 4) { // indented code block
280
- break;
281
- }
282
- if (fencesBeginRegex.test(line)) {
283
- break;
284
- }
285
- if (headingBeginRegex.test(line)) {
286
- break;
287
- }
288
- if (hrRegex.test(line)) {
289
- break;
290
- }
291
-
292
- itemContents += '\n' + nextLine;
293
- }
294
-
295
- if (!blankLine && !nextLine.trim()) { // Check if current line is blank
296
- blankLine = true;
297
- }
298
-
299
- raw += rawLine + '\n';
300
- src = src.substring(rawLine.length + 1);
301
- line = nextLine.slice(indent);
302
- }
303
- }
304
-
305
- if (!list.loose) {
306
- // If the previous item ended with a blank line, the list is loose
307
- if (endsWithBlankLine) {
308
- list.loose = true;
309
- } else if (/\n *\n *$/.test(raw)) {
310
- endsWithBlankLine = true;
311
- }
312
- }
313
-
314
- // Check for task list items
315
- if (this.options.gfm) {
316
- istask = /^\[[ xX]\] /.exec(itemContents);
317
- if (istask) {
318
- ischecked = istask[0] !== '[ ] ';
319
- itemContents = itemContents.replace(/^\[[ xX]\] +/, '');
320
- }
321
- }
322
-
323
- list.items.push({
324
- type: 'list_item',
325
- raw,
326
- task: !!istask,
327
- checked: ischecked,
328
- loose: false,
329
- text: itemContents
330
- });
331
-
332
- list.raw += raw;
333
- }
334
-
335
- // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
336
- list.items[list.items.length - 1].raw = raw.trimRight();
337
- (list.items[list.items.length - 1] as Tokens.ListItem).text = itemContents.trimRight();
338
- list.raw = list.raw.trimRight();
339
-
340
- const l = list.items.length;
341
-
342
- // Item child tokens handled here at end because we needed to have the final item to trim it first
343
- for (i = 0; i < l; i++) {
344
- this.lexer.state.top = false;
345
- list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);
346
-
347
- if (!list.loose) {
348
- // Check if list should be loose
349
- const spacers = list.items[i].tokens!.filter(t => t.type === 'space');
350
- const hasMultipleLineBreaks = spacers.length > 0 && spacers.some(t => /\n.*\n/.test(t.raw!));
351
-
352
- list.loose = hasMultipleLineBreaks;
353
- }
354
- }
355
-
356
- // Set all items to loose if list is loose
357
- if (list.loose) {
358
- for (i = 0; i < l; i++) {
359
- list.items[i].loose = true;
360
- }
361
- }
362
-
363
- return list;
364
- }
365
- }
366
-
367
- html(src: string): Tokens.HTML | Tokens.Paragraph | undefined {
368
- const cap = this.rules.block.html.exec(src);
369
- if (cap) {
370
- const token: Tokens.HTML | Tokens.Paragraph = {
371
- type: 'html',
372
- block: true,
373
- raw: cap[0],
374
- pre: !this.options.sanitizer
375
- && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
376
- text: cap[0]
377
- };
378
- if (this.options.sanitize) {
379
- const text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]);
380
- const paragraph = token as unknown as Tokens.Paragraph;
381
- paragraph.type = 'paragraph';
382
- paragraph.text = text;
383
- paragraph.tokens = this.lexer.inline(text);
384
- }
385
- return token;
386
- }
387
- }
388
-
389
- def(src: string): Tokens.Def | undefined {
390
- const cap = this.rules.block.def.exec(src);
391
- if (cap) {
392
- const tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
393
- const href = cap[2] ? cap[2].replace(/^<(.*)>$/, '$1').replace(this.rules.inline._escapes, '$1') : '';
394
- const title = cap[3] ? cap[3].substring(1, cap[3].length - 1).replace(this.rules.inline._escapes, '$1') : cap[3];
395
- return {
396
- type: 'def',
397
- tag,
398
- raw: cap[0],
399
- href,
400
- title
401
- };
402
- }
403
- }
404
-
405
- table(src: string): Tokens.Table | undefined {
406
- const cap = this.rules.block.table.exec(src);
407
- if (cap) {
408
- const item: Tokens.Table = {
409
- type: 'table',
410
- // splitCells expects a number as second argument
411
- // @ts-expect-error
412
- header: splitCells(cap[1]).map(c => {
413
- return { text: c };
414
- }),
415
- align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
416
- rows: cap[3] && cap[3].trim() ? cap[3].replace(/\n[ \t]*$/, '').split('\n') : []
417
- };
418
-
419
- if (item.header.length === item.align.length) {
420
- item.raw = cap[0];
421
-
422
- let l = item.align.length;
423
- let i, j, k, row;
424
- for (i = 0; i < l; i++) {
425
- if (/^ *-+: *$/.test(item.align[i]!)) {
426
- item.align[i] = 'right';
427
- } else if (/^ *:-+: *$/.test(item.align[i]!)) {
428
- item.align[i] = 'center';
429
- } else if (/^ *:-+ *$/.test(item.align[i]!)) {
430
- item.align[i] = 'left';
431
- } else {
432
- item.align[i] = null;
433
- }
434
- }
435
-
436
- l = item.rows.length;
437
- for (i = 0; i < l; i++) {
438
- item.rows[i] = splitCells(item.rows[i] as unknown as string, item.header.length).map(c => {
439
- return { text: c };
440
- });
441
- }
442
-
443
- // parse child tokens inside headers and cells
444
-
445
- // header child tokens
446
- l = item.header.length;
447
- for (j = 0; j < l; j++) {
448
- item.header[j].tokens = this.lexer.inline(item.header[j].text);
449
- }
450
-
451
- // cell child tokens
452
- l = item.rows.length;
453
- for (j = 0; j < l; j++) {
454
- row = item.rows[j];
455
- for (k = 0; k < row.length; k++) {
456
- row[k].tokens = this.lexer.inline(row[k].text);
457
- }
458
- }
459
-
460
- return item;
461
- }
462
- }
463
- }
464
-
465
- lheading(src: string): Tokens.Heading | undefined {
466
- const cap = this.rules.block.lheading.exec(src);
467
- if (cap) {
468
- return {
469
- type: 'heading',
470
- raw: cap[0],
471
- depth: cap[2].charAt(0) === '=' ? 1 : 2,
472
- text: cap[1],
473
- tokens: this.lexer.inline(cap[1])
474
- };
475
- }
476
- }
477
-
478
- paragraph(src: string): Tokens.Paragraph | undefined {
479
- const cap = this.rules.block.paragraph.exec(src);
480
- if (cap) {
481
- const text = cap[1].charAt(cap[1].length - 1) === '\n'
482
- ? cap[1].slice(0, -1)
483
- : cap[1];
484
- return {
485
- type: 'paragraph',
486
- raw: cap[0],
487
- text,
488
- tokens: this.lexer.inline(text)
489
- };
490
- }
491
- }
492
-
493
- text(src: string): Tokens.Text | undefined {
494
- const cap = this.rules.block.text.exec(src);
495
- if (cap) {
496
- return {
497
- type: 'text',
498
- raw: cap[0],
499
- text: cap[0],
500
- tokens: this.lexer.inline(cap[0])
501
- };
502
- }
503
- }
504
-
505
- escape(src: string): Tokens.Escape | undefined {
506
- const cap = this.rules.inline.escape.exec(src);
507
- if (cap) {
508
- return {
509
- type: 'escape',
510
- raw: cap[0],
511
- text: escape(cap[1])
512
- };
513
- }
514
- }
515
-
516
- tag(src: string): Tokens.Tag | undefined {
517
- const cap = this.rules.inline.tag.exec(src);
518
- if (cap) {
519
- if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {
520
- this.lexer.state.inLink = true;
521
- } else if (this.lexer.state.inLink && /^<\/a>/i.test(cap[0])) {
522
- this.lexer.state.inLink = false;
523
- }
524
- if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
525
- this.lexer.state.inRawBlock = true;
526
- } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
527
- this.lexer.state.inRawBlock = false;
528
- }
529
-
530
- return {
531
- type: this.options.sanitize
532
- ? 'text'
533
- : 'html',
534
- raw: cap[0],
535
- inLink: this.lexer.state.inLink,
536
- inRawBlock: this.lexer.state.inRawBlock,
537
- block: false,
538
- text: this.options.sanitize
539
- ? (this.options.sanitizer
540
- ? this.options.sanitizer(cap[0])
541
- : escape(cap[0]))
542
- : cap[0]
543
- };
544
- }
545
- }
546
-
547
- link(src: string): Tokens.Link | Tokens.Image | undefined {
548
- const cap = this.rules.inline.link.exec(src);
549
- if (cap) {
550
- const trimmedUrl = cap[2].trim();
551
- if (!this.options.pedantic && /^</.test(trimmedUrl)) {
552
- // commonmark requires matching angle brackets
553
- if (!(/>$/.test(trimmedUrl))) {
554
- return;
555
- }
556
-
557
- // ending angle bracket cannot be escaped
558
- const rtrimSlash = rtrim(trimmedUrl.slice(0, -1), '\\');
559
- if ((trimmedUrl.length - rtrimSlash.length) % 2 === 0) {
560
- return;
561
- }
562
- } else {
563
- // find closing parenthesis
564
- const lastParenIndex = findClosingBracket(cap[2], '()');
565
- if (lastParenIndex > -1) {
566
- const start = cap[0].indexOf('!') === 0 ? 5 : 4;
567
- const linkLen = start + cap[1].length + lastParenIndex;
568
- cap[2] = cap[2].substring(0, lastParenIndex);
569
- cap[0] = cap[0].substring(0, linkLen).trim();
570
- cap[3] = '';
571
- }
572
- }
573
- let href = cap[2];
574
- let title = '';
575
- if (this.options.pedantic) {
576
- // split pedantic href and title
577
- const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
578
-
579
- if (link) {
580
- href = link[1];
581
- title = link[3];
582
- }
583
- } else {
584
- title = cap[3] ? cap[3].slice(1, -1) : '';
585
- }
586
-
587
- href = href.trim();
588
- if (/^</.test(href)) {
589
- if (this.options.pedantic && !(/>$/.test(trimmedUrl))) {
590
- // pedantic allows starting angle bracket without ending angle bracket
591
- href = href.slice(1);
592
- } else {
593
- href = href.slice(1, -1);
594
- }
595
- }
596
- return outputLink(cap, {
597
- href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
598
- title: title ? title.replace(this.rules.inline._escapes, '$1') : title
599
- }, cap[0], this.lexer);
600
- }
601
- }
602
-
603
- reflink(src: string, links: Links): Tokens.Link | Tokens.Image | Tokens.Text | undefined {
604
- let cap;
605
- if ((cap = this.rules.inline.reflink.exec(src))
606
- || (cap = this.rules.inline.nolink.exec(src))) {
607
- let link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
608
- link = links[link.toLowerCase()];
609
- if (!link) {
610
- const text = cap[0].charAt(0);
611
- return {
612
- type: 'text',
613
- raw: text,
614
- text
615
- };
616
- }
617
- return outputLink(cap, link, cap[0], this.lexer);
618
- }
619
- }
620
-
621
- emStrong(src: string, maskedSrc: string, prevChar = ''): Tokens.Em | Tokens.Strong | undefined {
622
- let match = this.rules.inline.emStrong.lDelim.exec(src);
623
- if (!match) return;
624
-
625
- // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
626
- if (match[3] && prevChar.match(/[\p{L}\p{N}]/u)) return;
627
-
628
- const nextChar = match[1] || match[2] || '';
629
-
630
- if (!nextChar || !prevChar || this.rules.inline.punctuation.exec(prevChar)) {
631
- const lLength = match[0].length - 1;
632
- let rDelim, rLength, delimTotal = lLength, midDelimTotal = 0;
633
-
634
- const endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;
635
- endReg.lastIndex = 0;
636
-
637
- // Clip maskedSrc to same section of string as src (move to lexer?)
638
- maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
639
-
640
- while ((match = endReg.exec(maskedSrc)) != null) {
641
- rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
642
-
643
- if (!rDelim) continue; // skip single * in __abc*abc__
644
-
645
- rLength = rDelim.length;
646
-
647
- if (match[3] || match[4]) { // found another Left Delim
648
- delimTotal += rLength;
649
- continue;
650
- } else if (match[5] || match[6]) { // either Left or Right Delim
651
- if (lLength % 3 && !((lLength + rLength) % 3)) {
652
- midDelimTotal += rLength;
653
- continue; // CommonMark Emphasis Rules 9-10
654
- }
655
- }
656
-
657
- delimTotal -= rLength;
658
-
659
- if (delimTotal > 0) continue; // Haven't found enough closing delimiters
660
-
661
- // Remove extra characters. *a*** -> *a*
662
- rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
663
-
664
- const raw = src.slice(0, lLength + match.index + rLength + 1);
665
-
666
- // Create `em` if smallest delimiter has odd char count. *a***
667
- if (Math.min(lLength, rLength) % 2) {
668
- const text = raw.slice(1, -1);
669
- return {
670
- type: 'em',
671
- raw,
672
- text,
673
- tokens: this.lexer.inlineTokens(text)
674
- };
675
- }
676
-
677
- // Create 'strong' if smallest delimiter has even char count. **a***
678
- const text = raw.slice(2, -2);
679
- return {
680
- type: 'strong',
681
- raw,
682
- text,
683
- tokens: this.lexer.inlineTokens(text)
684
- };
685
- }
686
- }
687
- }
688
-
689
- codespan(src: string): Tokens.Codespan | undefined {
690
- const cap = this.rules.inline.code.exec(src);
691
- if (cap) {
692
- let text = cap[2].replace(/\n/g, ' ');
693
- const hasNonSpaceChars = /[^ ]/.test(text);
694
- const hasSpaceCharsOnBothEnds = /^ /.test(text) && / $/.test(text);
695
- if (hasNonSpaceChars && hasSpaceCharsOnBothEnds) {
696
- text = text.substring(1, text.length - 1);
697
- }
698
- text = escape(text, true);
699
- return {
700
- type: 'codespan',
701
- raw: cap[0],
702
- text
703
- };
704
- }
705
- }
706
-
707
- br(src: string): Tokens.Br | undefined {
708
- const cap = this.rules.inline.br.exec(src);
709
- if (cap) {
710
- return {
711
- type: 'br',
712
- raw: cap[0]
713
- };
714
- }
715
- }
716
-
717
- del(src: string): Tokens.Del | undefined {
718
- const cap = this.rules.inline.del.exec(src);
719
- if (cap) {
720
- return {
721
- type: 'del',
722
- raw: cap[0],
723
- text: cap[2],
724
- tokens: this.lexer.inlineTokens(cap[2])
725
- };
726
- }
727
- }
728
-
729
- autolink(src: string, mangle: (cap: string) => string): Tokens.Link | undefined {
730
- const cap = this.rules.inline.autolink.exec(src);
731
- if (cap) {
732
- let text, href;
733
- if (cap[2] === '@') {
734
- text = escape(this.options.mangle ? mangle(cap[1]) : cap[1]);
735
- href = 'mailto:' + text;
736
- } else {
737
- text = escape(cap[1]);
738
- href = text;
739
- }
740
-
741
- return {
742
- type: 'link',
743
- raw: cap[0],
744
- text,
745
- href,
746
- tokens: [
747
- {
748
- type: 'text',
749
- raw: text,
750
- text
751
- }
752
- ]
753
- };
754
- }
755
- }
756
-
757
- url(src: string, mangle: (cap: string) => string): Tokens.Link | undefined {
758
- let cap;
759
- if (cap = this.rules.inline.url.exec(src)) {
760
- let text, href;
761
- if (cap[2] === '@') {
762
- text = escape(this.options.mangle ? mangle(cap[0]) : cap[0]);
763
- href = 'mailto:' + text;
764
- } else {
765
- // do extended autolink path validation
766
- let prevCapZero;
767
- do {
768
- prevCapZero = cap[0];
769
- cap[0] = this.rules.inline._backpedal.exec(cap[0])[0];
770
- } while (prevCapZero !== cap[0]);
771
- text = escape(cap[0]);
772
- if (cap[1] === 'www.') {
773
- href = 'http://' + cap[0];
774
- } else {
775
- href = cap[0];
776
- }
777
- }
778
- return {
779
- type: 'link',
780
- raw: cap[0],
781
- text,
782
- href,
783
- tokens: [
784
- {
785
- type: 'text',
786
- raw: text,
787
- text
788
- }
789
- ]
790
- };
791
- }
792
- }
793
-
794
- inlineText(src: string, smartypants: (cap: string) => string): Tokens.Text | undefined {
795
- const cap = this.rules.inline.text.exec(src);
796
- if (cap) {
797
- let text;
798
- if (this.lexer.state.inRawBlock) {
799
- text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0];
800
- } else {
801
- text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
802
- }
803
- return {
804
- type: 'text',
805
- raw: cap[0],
806
- text
807
- };
808
- }
809
- }
810
- }