marked 2.1.3 → 3.0.3

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/Parser.js CHANGED
@@ -66,7 +66,7 @@ module.exports = class Parser {
66
66
 
67
67
  // Run any renderer extensions
68
68
  if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
69
- ret = this.options.extensions.renderers[token.type].call(this, token);
69
+ ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);
70
70
  if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(token.type)) {
71
71
  out += ret || '';
72
72
  continue;
@@ -103,22 +103,22 @@ module.exports = class Parser {
103
103
  l2 = token.header.length;
104
104
  for (j = 0; j < l2; j++) {
105
105
  cell += this.renderer.tablecell(
106
- this.parseInline(token.tokens.header[j]),
106
+ this.parseInline(token.header[j].tokens),
107
107
  { header: true, align: token.align[j] }
108
108
  );
109
109
  }
110
110
  header += this.renderer.tablerow(cell);
111
111
 
112
112
  body = '';
113
- l2 = token.cells.length;
113
+ l2 = token.rows.length;
114
114
  for (j = 0; j < l2; j++) {
115
- row = token.tokens.cells[j];
115
+ row = token.rows[j];
116
116
 
117
117
  cell = '';
118
118
  l3 = row.length;
119
119
  for (k = 0; k < l3; k++) {
120
120
  cell += this.renderer.tablecell(
121
- this.parseInline(row[k]),
121
+ this.parseInline(row[k].tokens),
122
122
  { header: false, align: token.align[k] }
123
123
  );
124
124
  }
@@ -149,7 +149,7 @@ module.exports = class Parser {
149
149
  if (item.task) {
150
150
  checkbox = this.renderer.checkbox(checked);
151
151
  if (loose) {
152
- if (item.tokens.length > 0 && item.tokens[0].type === 'text') {
152
+ if (item.tokens.length > 0 && item.tokens[0].type === 'paragraph') {
153
153
  item.tokens[0].text = checkbox + ' ' + item.tokens[0].text;
154
154
  if (item.tokens[0].tokens && item.tokens[0].tokens.length > 0 && item.tokens[0].tokens[0].type === 'text') {
155
155
  item.tokens[0].tokens[0].text = checkbox + ' ' + item.tokens[0].tokens[0].text;
@@ -222,7 +222,7 @@ module.exports = class Parser {
222
222
 
223
223
  // Run any renderer extensions
224
224
  if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
225
- ret = this.options.extensions.renderers[token.type].call(this, token);
225
+ ret = this.options.extensions.renderers[token.type].call({ parser: this }, token);
226
226
  if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {
227
227
  out += ret || '';
228
228
  continue;
package/src/Tokenizer.js CHANGED
@@ -6,19 +6,23 @@ const {
6
6
  findClosingBracket
7
7
  } = require('./helpers.js');
8
8
 
9
- function outputLink(cap, link, raw) {
9
+ function outputLink(cap, link, raw, lexer) {
10
10
  const href = link.href;
11
11
  const title = link.title ? escape(link.title) : null;
12
12
  const text = cap[1].replace(/\\([\[\]])/g, '$1');
13
13
 
14
14
  if (cap[0].charAt(0) !== '!') {
15
- return {
15
+ lexer.state.inLink = true;
16
+ const token = {
16
17
  type: 'link',
17
18
  raw,
18
19
  href,
19
20
  title,
20
- text
21
+ text,
22
+ tokens: lexer.inlineTokens(text, [])
21
23
  };
24
+ lexer.state.inLink = false;
25
+ return token;
22
26
  } else {
23
27
  return {
24
28
  type: 'image',
@@ -125,48 +129,15 @@ module.exports = class Tokenizer {
125
129
  }
126
130
  }
127
131
 
128
- return {
132
+ const token = {
129
133
  type: 'heading',
130
134
  raw: cap[0],
131
135
  depth: cap[1].length,
132
- text: text
136
+ text: text,
137
+ tokens: []
133
138
  };
134
- }
135
- }
136
-
137
- nptable(src) {
138
- const cap = this.rules.block.nptable.exec(src);
139
- if (cap) {
140
- const item = {
141
- type: 'table',
142
- header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
143
- align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
144
- cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [],
145
- raw: cap[0]
146
- };
147
-
148
- if (item.header.length === item.align.length) {
149
- let l = item.align.length;
150
- let i;
151
- for (i = 0; i < l; i++) {
152
- if (/^ *-+: *$/.test(item.align[i])) {
153
- item.align[i] = 'right';
154
- } else if (/^ *:-+: *$/.test(item.align[i])) {
155
- item.align[i] = 'center';
156
- } else if (/^ *:-+ *$/.test(item.align[i])) {
157
- item.align[i] = 'left';
158
- } else {
159
- item.align[i] = null;
160
- }
161
- }
162
-
163
- l = item.cells.length;
164
- for (i = 0; i < l; i++) {
165
- item.cells[i] = splitCells(item.cells[i], item.header.length);
166
- }
167
-
168
- return item;
169
- }
139
+ this.lexer.inline(token.text, token.tokens);
140
+ return token;
170
141
  }
171
142
  }
172
143
 
@@ -188,138 +159,155 @@ module.exports = class Tokenizer {
188
159
  return {
189
160
  type: 'blockquote',
190
161
  raw: cap[0],
162
+ tokens: this.lexer.blockTokens(text, []),
191
163
  text
192
164
  };
193
165
  }
194
166
  }
195
167
 
196
168
  list(src) {
197
- const cap = this.rules.block.list.exec(src);
169
+ let cap = this.rules.block.list.exec(src);
198
170
  if (cap) {
199
- let raw = cap[0];
200
- const bull = cap[2];
171
+ let raw, istask, ischecked, indent, i, blankLine, endsWithBlankLine,
172
+ line, lines, itemContents;
173
+
174
+ let bull = cap[1].trim();
201
175
  const isordered = bull.length > 1;
202
176
 
203
177
  const list = {
204
178
  type: 'list',
205
- raw,
179
+ raw: '',
206
180
  ordered: isordered,
207
181
  start: isordered ? +bull.slice(0, -1) : '',
208
182
  loose: false,
209
183
  items: []
210
184
  };
211
185
 
212
- // Get each top-level item.
213
- const itemMatch = cap[0].match(this.rules.block.item);
214
-
215
- let next = false,
216
- item,
217
- space,
218
- bcurr,
219
- bnext,
220
- addBack,
221
- loose,
222
- istask,
223
- ischecked,
224
- endMatch;
225
-
226
- let l = itemMatch.length;
227
- bcurr = this.rules.block.listItemStart.exec(itemMatch[0]);
228
- for (let i = 0; i < l; i++) {
229
- item = itemMatch[i];
230
- raw = item;
231
-
232
- if (!this.options.pedantic) {
233
- // Determine if current item contains the end of the list
234
- endMatch = item.match(new RegExp('\\n\\s*\\n {0,' + (bcurr[0].length - 1) + '}\\S'));
235
- if (endMatch) {
236
- addBack = item.length - endMatch.index + itemMatch.slice(i + 1).join('\n').length;
237
- list.raw = list.raw.substring(0, list.raw.length - addBack);
238
-
239
- item = item.substring(0, endMatch.index);
240
- raw = item;
241
- l = i + 1;
242
- }
186
+ bull = isordered ? `\\d{1,9}\\${bull.slice(-1)}` : `\\${bull}`;
187
+
188
+ if (this.options.pedantic) {
189
+ bull = isordered ? bull : '[*+-]';
190
+ }
191
+
192
+ // Get next list item
193
+ const itemRegex = new RegExp(`^( {0,3}${bull})((?: [^\\n]*| *)(?:\\n[^\\n]*)*(?:\\n|$))`);
194
+
195
+ // Get each top-level item
196
+ while (src) {
197
+ if (this.rules.block.hr.test(src)) { // End list if we encounter an HR (possibly move into itemRegex?)
198
+ break;
243
199
  }
244
200
 
245
- // Determine whether the next list item belongs here.
246
- // Backpedal if it does not belong in this list.
247
- if (i !== l - 1) {
248
- bnext = this.rules.block.listItemStart.exec(itemMatch[i + 1]);
249
- if (
250
- !this.options.pedantic
251
- ? bnext[1].length >= bcurr[0].length || bnext[1].length > 3
252
- : bnext[1].length > bcurr[1].length
253
- ) {
254
- // nested list or continuation
255
- itemMatch.splice(i, 2, itemMatch[i] + (!this.options.pedantic && bnext[1].length < bcurr[0].length && !itemMatch[i].match(/\n$/) ? '' : '\n') + itemMatch[i + 1]);
256
- i--;
257
- l--;
258
- continue;
259
- } else if (
260
- // different bullet style
261
- !this.options.pedantic || this.options.smartLists
262
- ? bnext[2][bnext[2].length - 1] !== bull[bull.length - 1]
263
- : isordered === (bnext[2].length === 1)
264
- ) {
265
- addBack = itemMatch.slice(i + 1).join('\n').length;
266
- list.raw = list.raw.substring(0, list.raw.length - addBack);
267
- i = l - 1;
268
- }
269
- bcurr = bnext;
201
+ if (!(cap = itemRegex.exec(src))) {
202
+ break;
270
203
  }
271
204
 
272
- // Remove the list item's bullet
273
- // so it is seen as the next token.
274
- space = item.length;
275
- item = item.replace(/^ *([*+-]|\d+[.)]) ?/, '');
276
-
277
- // Outdent whatever the
278
- // list item contains. Hacky.
279
- if (~item.indexOf('\n ')) {
280
- space -= item.length;
281
- item = !this.options.pedantic
282
- ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
283
- : item.replace(/^ {1,4}/gm, '');
205
+ lines = cap[2].split('\n');
206
+
207
+ if (this.options.pedantic) {
208
+ indent = 2;
209
+ itemContents = lines[0].trimLeft();
210
+ } else {
211
+ indent = cap[2].search(/[^ ]/); // Find first non-space char
212
+ indent = cap[1].length + (indent > 4 ? 1 : indent); // intented code blocks after 4 spaces; indent is always 1
213
+ itemContents = lines[0].slice(indent - cap[1].length);
284
214
  }
285
215
 
286
- // trim item newlines at end
287
- item = rtrim(item, '\n');
288
- if (i !== l - 1) {
289
- raw = raw + '\n';
216
+ blankLine = false;
217
+ raw = cap[0];
218
+
219
+ if (!lines[0] && /^ *$/.test(lines[1])) { // items begin with at most one blank line
220
+ raw = cap[1] + lines.slice(0, 2).join('\n') + '\n';
221
+ list.loose = true;
222
+ lines = [];
290
223
  }
291
224
 
292
- // Determine whether item is loose or not.
293
- // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
294
- // for discount behavior.
295
- loose = next || /\n\n(?!\s*$)/.test(raw);
296
- if (i !== l - 1) {
297
- next = raw.slice(-2) === '\n\n';
298
- if (!loose) loose = next;
225
+ const nextBulletRegex = new RegExp(`^ {0,${Math.min(3, indent - 1)}}(?:[*+-]|\\d{1,9}[.)])`);
226
+
227
+ for (i = 1; i < lines.length; i++) {
228
+ line = lines[i];
229
+
230
+ if (this.options.pedantic) { // Re-align to follow commonmark nesting rules
231
+ line = line.replace(/^ {1,4}(?=( {4})*[^ ])/g, ' ');
232
+ }
233
+
234
+ // End list item if found start of new bullet
235
+ if (nextBulletRegex.test(line)) {
236
+ raw = cap[1] + lines.slice(0, i).join('\n') + '\n';
237
+ break;
238
+ }
239
+
240
+ // Until we encounter a blank line, item contents do not need indentation
241
+ if (!blankLine) {
242
+ if (!line.trim()) { // Check if current line is empty
243
+ blankLine = true;
244
+ }
245
+
246
+ // Dedent if possible
247
+ if (line.search(/[^ ]/) >= indent) {
248
+ itemContents += '\n' + line.slice(indent);
249
+ } else {
250
+ itemContents += '\n' + line;
251
+ }
252
+ continue;
253
+ }
254
+
255
+ // Dedent this line
256
+ if (line.search(/[^ ]/) >= indent || !line.trim()) {
257
+ itemContents += '\n' + line.slice(indent);
258
+ continue;
259
+ } else { // Line was not properly indented; end of this item
260
+ raw = cap[1] + lines.slice(0, i).join('\n') + '\n';
261
+ break;
262
+ }
299
263
  }
300
264
 
301
- if (loose) {
302
- list.loose = true;
265
+ if (!list.loose) {
266
+ // If the previous item ended with a blank line, the list is loose
267
+ if (endsWithBlankLine) {
268
+ list.loose = true;
269
+ } else if (/\n *\n *$/.test(raw)) {
270
+ endsWithBlankLine = true;
271
+ }
303
272
  }
304
273
 
305
274
  // Check for task list items
306
275
  if (this.options.gfm) {
307
- istask = /^\[[ xX]\] /.test(item);
308
- ischecked = undefined;
276
+ istask = /^\[[ xX]\] /.exec(itemContents);
309
277
  if (istask) {
310
- ischecked = item[1] !== ' ';
311
- item = item.replace(/^\[[ xX]\] +/, '');
278
+ ischecked = istask[0] !== '[ ] ';
279
+ itemContents = itemContents.replace(/^\[[ xX]\] +/, '');
312
280
  }
313
281
  }
314
282
 
315
283
  list.items.push({
316
284
  type: 'list_item',
317
- raw,
318
- task: istask,
285
+ raw: raw,
286
+ task: !!istask,
319
287
  checked: ischecked,
320
- loose: loose,
321
- text: item
288
+ loose: false,
289
+ text: itemContents
322
290
  });
291
+
292
+ list.raw += raw;
293
+ src = src.slice(raw.length);
294
+ }
295
+
296
+ // Do not consume newlines at end of final item. Alternatively, make itemRegex *start* with any newlines to simplify/speed up endsWithBlankLine logic
297
+ list.items[list.items.length - 1].raw = raw.trimRight();
298
+ list.items[list.items.length - 1].text = itemContents.trimRight();
299
+ list.raw = list.raw.trimRight();
300
+
301
+ const l = list.items.length;
302
+
303
+ // Item child tokens handled here at end because we needed to have the final item to trim it first
304
+ for (i = 0; i < l; i++) {
305
+ this.lexer.state.top = false;
306
+ list.items[i].tokens = this.lexer.blockTokens(list.items[i].text, []);
307
+ if (list.items[i].tokens.some(t => t.type === 'space')) {
308
+ list.loose = true;
309
+ list.items[i].loose = true;
310
+ }
323
311
  }
324
312
 
325
313
  return list;
@@ -329,15 +317,20 @@ module.exports = class Tokenizer {
329
317
  html(src) {
330
318
  const cap = this.rules.block.html.exec(src);
331
319
  if (cap) {
332
- return {
333
- type: this.options.sanitize
334
- ? 'paragraph'
335
- : 'html',
320
+ const token = {
321
+ type: 'html',
336
322
  raw: cap[0],
337
323
  pre: !this.options.sanitizer
338
324
  && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
339
- text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0]
325
+ text: cap[0]
340
326
  };
327
+ if (this.options.sanitize) {
328
+ token.type = 'paragraph';
329
+ token.text = this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0]);
330
+ token.tokens = [];
331
+ this.lexer.inline(token.text, token.tokens);
332
+ }
333
+ return token;
341
334
  }
342
335
  }
343
336
 
@@ -361,16 +354,16 @@ module.exports = class Tokenizer {
361
354
  if (cap) {
362
355
  const item = {
363
356
  type: 'table',
364
- header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
357
+ header: splitCells(cap[1]).map(c => { return { text: c }; }),
365
358
  align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
366
- cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
359
+ rows: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
367
360
  };
368
361
 
369
362
  if (item.header.length === item.align.length) {
370
363
  item.raw = cap[0];
371
364
 
372
365
  let l = item.align.length;
373
- let i;
366
+ let i, j, k, row;
374
367
  for (i = 0; i < l; i++) {
375
368
  if (/^ *-+: *$/.test(item.align[i])) {
376
369
  item.align[i] = 'right';
@@ -383,11 +376,28 @@ module.exports = class Tokenizer {
383
376
  }
384
377
  }
385
378
 
386
- l = item.cells.length;
379
+ l = item.rows.length;
387
380
  for (i = 0; i < l; i++) {
388
- item.cells[i] = splitCells(
389
- item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
390
- item.header.length);
381
+ item.rows[i] = splitCells(item.rows[i], item.header.length).map(c => { return { text: c }; });
382
+ }
383
+
384
+ // parse child tokens inside headers and cells
385
+
386
+ // header child tokens
387
+ l = item.header.length;
388
+ for (j = 0; j < l; j++) {
389
+ item.header[j].tokens = [];
390
+ this.lexer.inlineTokens(item.header[j].text, item.header[j].tokens);
391
+ }
392
+
393
+ // cell child tokens
394
+ l = item.rows.length;
395
+ for (j = 0; j < l; j++) {
396
+ row = item.rows[j];
397
+ for (k = 0; k < row.length; k++) {
398
+ row[k].tokens = [];
399
+ this.lexer.inlineTokens(row[k].text, row[k].tokens);
400
+ }
391
401
  }
392
402
 
393
403
  return item;
@@ -398,36 +408,45 @@ module.exports = class Tokenizer {
398
408
  lheading(src) {
399
409
  const cap = this.rules.block.lheading.exec(src);
400
410
  if (cap) {
401
- return {
411
+ const token = {
402
412
  type: 'heading',
403
413
  raw: cap[0],
404
414
  depth: cap[2].charAt(0) === '=' ? 1 : 2,
405
- text: cap[1]
415
+ text: cap[1],
416
+ tokens: []
406
417
  };
418
+ this.lexer.inline(token.text, token.tokens);
419
+ return token;
407
420
  }
408
421
  }
409
422
 
410
423
  paragraph(src) {
411
424
  const cap = this.rules.block.paragraph.exec(src);
412
425
  if (cap) {
413
- return {
426
+ const token = {
414
427
  type: 'paragraph',
415
428
  raw: cap[0],
416
429
  text: cap[1].charAt(cap[1].length - 1) === '\n'
417
430
  ? cap[1].slice(0, -1)
418
- : cap[1]
431
+ : cap[1],
432
+ tokens: []
419
433
  };
434
+ this.lexer.inline(token.text, token.tokens);
435
+ return token;
420
436
  }
421
437
  }
422
438
 
423
439
  text(src) {
424
440
  const cap = this.rules.block.text.exec(src);
425
441
  if (cap) {
426
- return {
442
+ const token = {
427
443
  type: 'text',
428
444
  raw: cap[0],
429
- text: cap[0]
445
+ text: cap[0],
446
+ tokens: []
430
447
  };
448
+ this.lexer.inline(token.text, token.tokens);
449
+ return token;
431
450
  }
432
451
  }
433
452
 
@@ -442,18 +461,18 @@ module.exports = class Tokenizer {
442
461
  }
443
462
  }
444
463
 
445
- tag(src, inLink, inRawBlock) {
464
+ tag(src) {
446
465
  const cap = this.rules.inline.tag.exec(src);
447
466
  if (cap) {
448
- if (!inLink && /^<a /i.test(cap[0])) {
449
- inLink = true;
450
- } else if (inLink && /^<\/a>/i.test(cap[0])) {
451
- inLink = false;
467
+ if (!this.lexer.state.inLink && /^<a /i.test(cap[0])) {
468
+ this.lexer.state.inLink = true;
469
+ } else if (this.lexer.state.inLink && /^<\/a>/i.test(cap[0])) {
470
+ this.lexer.state.inLink = false;
452
471
  }
453
- if (!inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
454
- inRawBlock = true;
455
- } else if (inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
456
- inRawBlock = false;
472
+ if (!this.lexer.state.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
473
+ this.lexer.state.inRawBlock = true;
474
+ } else if (this.lexer.state.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
475
+ this.lexer.state.inRawBlock = false;
457
476
  }
458
477
 
459
478
  return {
@@ -461,8 +480,8 @@ module.exports = class Tokenizer {
461
480
  ? 'text'
462
481
  : 'html',
463
482
  raw: cap[0],
464
- inLink,
465
- inRawBlock,
483
+ inLink: this.lexer.state.inLink,
484
+ inRawBlock: this.lexer.state.inRawBlock,
466
485
  text: this.options.sanitize
467
486
  ? (this.options.sanitizer
468
487
  ? this.options.sanitizer(cap[0])
@@ -524,7 +543,7 @@ module.exports = class Tokenizer {
524
543
  return outputLink(cap, {
525
544
  href: href ? href.replace(this.rules.inline._escapes, '$1') : href,
526
545
  title: title ? title.replace(this.rules.inline._escapes, '$1') : title
527
- }, cap[0]);
546
+ }, cap[0], this.lexer);
528
547
  }
529
548
  }
530
549
 
@@ -542,7 +561,7 @@ module.exports = class Tokenizer {
542
561
  text
543
562
  };
544
563
  }
545
- return outputLink(cap, link, cap[0]);
564
+ return outputLink(cap, link, cap[0], this.lexer);
546
565
  }
547
566
  }
548
567
 
@@ -591,18 +610,22 @@ module.exports = class Tokenizer {
591
610
 
592
611
  // Create `em` if smallest delimiter has odd char count. *a***
593
612
  if (Math.min(lLength, rLength) % 2) {
613
+ const text = src.slice(1, lLength + match.index + rLength);
594
614
  return {
595
615
  type: 'em',
596
616
  raw: src.slice(0, lLength + match.index + rLength + 1),
597
- text: src.slice(1, lLength + match.index + rLength)
617
+ text,
618
+ tokens: this.lexer.inlineTokens(text, [])
598
619
  };
599
620
  }
600
621
 
601
622
  // Create 'strong' if smallest delimiter has even char count. **a***
623
+ const text = src.slice(2, lLength + match.index + rLength - 1);
602
624
  return {
603
625
  type: 'strong',
604
626
  raw: src.slice(0, lLength + match.index + rLength + 1),
605
- text: src.slice(2, lLength + match.index + rLength - 1)
627
+ text,
628
+ tokens: this.lexer.inlineTokens(text, [])
606
629
  };
607
630
  }
608
631
  }
@@ -642,7 +665,8 @@ module.exports = class Tokenizer {
642
665
  return {
643
666
  type: 'del',
644
667
  raw: cap[0],
645
- text: cap[2]
668
+ text: cap[2],
669
+ tokens: this.lexer.inlineTokens(cap[2], [])
646
670
  };
647
671
  }
648
672
  }
@@ -712,11 +736,11 @@ module.exports = class Tokenizer {
712
736
  }
713
737
  }
714
738
 
715
- inlineText(src, inRawBlock, smartypants) {
739
+ inlineText(src, smartypants) {
716
740
  const cap = this.rules.inline.text.exec(src);
717
741
  if (cap) {
718
742
  let text;
719
- if (inRawBlock) {
743
+ if (this.lexer.state.inRawBlock) {
720
744
  text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0];
721
745
  } else {
722
746
  text = escape(this.options.smartypants ? smartypants(cap[0]) : cap[0]);
package/src/helpers.js CHANGED
@@ -160,6 +160,10 @@ function splitCells(tableRow, count) {
160
160
  cells = row.split(/ \|/);
161
161
  let i = 0;
162
162
 
163
+ // First/last cell in a row cannot be empty if it has no leading/trailing pipe
164
+ if (!cells[0].trim()) { cells.shift(); }
165
+ if (!cells[cells.length - 1].trim()) { cells.pop(); }
166
+
163
167
  if (cells.length > count) {
164
168
  cells.splice(count);
165
169
  } else {
package/src/marked.js CHANGED
@@ -260,12 +260,12 @@ marked.walkTokens = function(tokens, callback) {
260
260
  callback(token);
261
261
  switch (token.type) {
262
262
  case 'table': {
263
- for (const cell of token.tokens.header) {
264
- marked.walkTokens(cell, callback);
263
+ for (const cell of token.header) {
264
+ marked.walkTokens(cell.tokens, callback);
265
265
  }
266
- for (const row of token.tokens.cells) {
266
+ for (const row of token.rows) {
267
267
  for (const cell of row) {
268
- marked.walkTokens(cell, callback);
268
+ marked.walkTokens(cell.tokens, callback);
269
269
  }
270
270
  }
271
271
  break;