marked 2.0.6 → 2.1.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/lib/marked.esm.js CHANGED
@@ -15,6 +15,7 @@ function getDefaults$1() {
15
15
  return {
16
16
  baseUrl: null,
17
17
  breaks: false,
18
+ extensions: null,
18
19
  gfm: true,
19
20
  headerIds: true,
20
21
  headerPrefix: '',
@@ -858,7 +859,8 @@ var Tokenizer_1 = class Tokenizer {
858
859
  let match = this.rules.inline.emStrong.lDelim.exec(src);
859
860
  if (!match) return;
860
861
 
861
- if (match[3] && prevChar.match(/[\p{L}\p{N}]/u)) return; // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
862
+ // _ can't be between two alphanumerics. \p{L}\p{N} includes non-english alphabet/numbers as well
863
+ if (match[3] && prevChar.match(/[\p{L}\p{N}]/u)) return;
862
864
 
863
865
  const nextChar = match[1] || match[2] || '';
864
866
 
@@ -869,12 +871,13 @@ var Tokenizer_1 = class Tokenizer {
869
871
  const endReg = match[0][0] === '*' ? this.rules.inline.emStrong.rDelimAst : this.rules.inline.emStrong.rDelimUnd;
870
872
  endReg.lastIndex = 0;
871
873
 
872
- maskedSrc = maskedSrc.slice(-1 * src.length + lLength); // Bump maskedSrc to same section of string as src (move to lexer?)
874
+ // Clip maskedSrc to same section of string as src (move to lexer?)
875
+ maskedSrc = maskedSrc.slice(-1 * src.length + lLength);
873
876
 
874
877
  while ((match = endReg.exec(maskedSrc)) != null) {
875
878
  rDelim = match[1] || match[2] || match[3] || match[4] || match[5] || match[6];
876
879
 
877
- if (!rDelim) continue; // matched the first alternative in rules.js (skip the * in __abc*abc__)
880
+ if (!rDelim) continue; // skip single * in __abc*abc__
878
881
 
879
882
  rLength = rDelim.length;
880
883
 
@@ -892,11 +895,10 @@ var Tokenizer_1 = class Tokenizer {
892
895
 
893
896
  if (delimTotal > 0) continue; // Haven't found enough closing delimiters
894
897
 
895
- // If this is the last rDelimiter, remove extra characters. *a*** -> *a*
896
- if (delimTotal + midDelimTotal - rLength <= 0 && !maskedSrc.slice(endReg.lastIndex).match(endReg)) {
897
- rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
898
- }
898
+ // Remove extra characters. *a*** -> *a*
899
+ rLength = Math.min(rLength, rLength + delimTotal + midDelimTotal);
899
900
 
901
+ // Create `em` if smallest delimiter has odd char count. *a***
900
902
  if (Math.min(lLength, rLength) % 2) {
901
903
  return {
902
904
  type: 'em',
@@ -904,13 +906,13 @@ var Tokenizer_1 = class Tokenizer {
904
906
  text: src.slice(1, lLength + match.index + rLength)
905
907
  };
906
908
  }
907
- if (Math.min(lLength, rLength) % 2 === 0) {
908
- return {
909
- type: 'strong',
910
- raw: src.slice(0, lLength + match.index + rLength + 1),
911
- text: src.slice(2, lLength + match.index + rLength - 1)
912
- };
913
- }
909
+
910
+ // Create 'strong' if smallest delimiter has even char count. **a***
911
+ return {
912
+ type: 'strong',
913
+ raw: src.slice(0, lLength + match.index + rLength + 1),
914
+ text: src.slice(2, lLength + match.index + rLength - 1)
915
+ };
914
916
  }
915
917
  }
916
918
  }
@@ -1215,9 +1217,9 @@ const inline$1 = {
1215
1217
  emStrong: {
1216
1218
  lDelim: /^(?:\*+(?:([punct_])|[^\s*]))|^_+(?:([punct*])|([^\s_]))/,
1217
1219
  // (1) and (2) can only be a Right Delimiter. (3) and (4) can only be Left. (5) and (6) can be either Left or Right.
1218
- // () Skip other delimiter (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a
1219
- rDelimAst: /\_\_[^_]*?\*[^_]*?\_\_|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/,
1220
- rDelimUnd: /\*\*[^*]*?\_[^*]*?\*\*|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _
1220
+ // () Skip other delimiter (1) #*** (2) a***#, a*** (3) #***a, ***a (4) ***# (5) #***# (6) a***a
1221
+ rDelimAst: /\_\_[^_*]*?\*[^_*]*?\_\_|[punct_](\*+)(?=[\s]|$)|[^punct*_\s](\*+)(?=[punct_\s]|$)|[punct_\s](\*+)(?=[^punct*_\s])|[\s](\*+)(?=[punct_])|[punct_](\*+)(?=[punct_])|[^punct*_\s](\*+)(?=[^punct*_\s])/,
1222
+ rDelimUnd: /\*\*[^_*]*?\_[^_*]*?\*\*|[punct*](\_+)(?=[\s]|$)|[^punct*_\s](\_+)(?=[punct*\s]|$)|[punct*\s](\_+)(?=[^punct*_\s])|[\s](\_+)(?=[punct*])|[punct*](\_+)(?=[punct*])/ // ^- Not allowed for _
1221
1223
  },
1222
1224
  code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
1223
1225
  br: /^( {2,}|\\)\n(?!\s*$)/,
@@ -1473,9 +1475,22 @@ var Lexer_1 = class Lexer {
1473
1475
  if (this.options.pedantic) {
1474
1476
  src = src.replace(/^ +$/gm, '');
1475
1477
  }
1476
- let token, i, l, lastToken;
1478
+ let token, i, l, lastToken, cutSrc, lastParagraphClipped;
1477
1479
 
1478
1480
  while (src) {
1481
+ if (this.options.extensions
1482
+ && this.options.extensions.block
1483
+ && this.options.extensions.block.some((extTokenizer) => {
1484
+ if (token = extTokenizer.call(this, src, tokens)) {
1485
+ src = src.substring(token.raw.length);
1486
+ tokens.push(token);
1487
+ return true;
1488
+ }
1489
+ return false;
1490
+ })) {
1491
+ continue;
1492
+ }
1493
+
1479
1494
  // newline
1480
1495
  if (token = this.tokenizer.space(src)) {
1481
1496
  src = src.substring(token.raw.length);
@@ -1580,9 +1595,30 @@ var Lexer_1 = class Lexer {
1580
1595
  }
1581
1596
 
1582
1597
  // top-level paragraph
1583
- if (top && (token = this.tokenizer.paragraph(src))) {
1598
+ // prevent paragraph consuming extensions by clipping 'src' to extension start
1599
+ cutSrc = src;
1600
+ if (this.options.extensions && this.options.extensions.startBlock) {
1601
+ let startIndex = Infinity;
1602
+ const tempSrc = src.slice(1);
1603
+ let tempStart;
1604
+ this.options.extensions.startBlock.forEach(function(getStartIndex) {
1605
+ tempStart = getStartIndex.call(this, tempSrc);
1606
+ if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); }
1607
+ });
1608
+ if (startIndex < Infinity && startIndex >= 0) {
1609
+ cutSrc = src.substring(0, startIndex + 1);
1610
+ }
1611
+ }
1612
+ if (top && (token = this.tokenizer.paragraph(cutSrc))) {
1613
+ lastToken = tokens[tokens.length - 1];
1614
+ if (lastParagraphClipped && lastToken.type === 'paragraph') {
1615
+ lastToken.raw += '\n' + token.raw;
1616
+ lastToken.text += '\n' + token.text;
1617
+ } else {
1618
+ tokens.push(token);
1619
+ }
1620
+ lastParagraphClipped = (cutSrc.length !== src.length);
1584
1621
  src = src.substring(token.raw.length);
1585
- tokens.push(token);
1586
1622
  continue;
1587
1623
  }
1588
1624
 
@@ -1679,7 +1715,7 @@ var Lexer_1 = class Lexer {
1679
1715
  * Lexing/Compiling
1680
1716
  */
1681
1717
  inlineTokens(src, tokens = [], inLink = false, inRawBlock = false) {
1682
- let token, lastToken;
1718
+ let token, lastToken, cutSrc;
1683
1719
 
1684
1720
  // String with links masked to avoid interference with em and strong
1685
1721
  let maskedSrc = src;
@@ -1713,6 +1749,20 @@ var Lexer_1 = class Lexer {
1713
1749
  }
1714
1750
  keepPrevChar = false;
1715
1751
 
1752
+ // extensions
1753
+ if (this.options.extensions
1754
+ && this.options.extensions.inline
1755
+ && this.options.extensions.inline.some((extTokenizer) => {
1756
+ if (token = extTokenizer.call(this, src, tokens)) {
1757
+ src = src.substring(token.raw.length);
1758
+ tokens.push(token);
1759
+ return true;
1760
+ }
1761
+ return false;
1762
+ })) {
1763
+ continue;
1764
+ }
1765
+
1716
1766
  // escape
1717
1767
  if (token = this.tokenizer.escape(src)) {
1718
1768
  src = src.substring(token.raw.length);
@@ -1725,7 +1775,7 @@ var Lexer_1 = class Lexer {
1725
1775
  src = src.substring(token.raw.length);
1726
1776
  inLink = token.inLink;
1727
1777
  inRawBlock = token.inRawBlock;
1728
- const lastToken = tokens[tokens.length - 1];
1778
+ lastToken = tokens[tokens.length - 1];
1729
1779
  if (lastToken && token.type === 'text' && lastToken.type === 'text') {
1730
1780
  lastToken.raw += token.raw;
1731
1781
  lastToken.text += token.text;
@@ -1748,7 +1798,7 @@ var Lexer_1 = class Lexer {
1748
1798
  // reflink, nolink
1749
1799
  if (token = this.tokenizer.reflink(src, this.tokens.links)) {
1750
1800
  src = src.substring(token.raw.length);
1751
- const lastToken = tokens[tokens.length - 1];
1801
+ lastToken = tokens[tokens.length - 1];
1752
1802
  if (token.type === 'link') {
1753
1803
  token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
1754
1804
  tokens.push(token);
@@ -1806,7 +1856,21 @@ var Lexer_1 = class Lexer {
1806
1856
  }
1807
1857
 
1808
1858
  // text
1809
- if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
1859
+ // prevent inlineText consuming extensions by clipping 'src' to extension start
1860
+ cutSrc = src;
1861
+ if (this.options.extensions && this.options.extensions.startInline) {
1862
+ let startIndex = Infinity;
1863
+ const tempSrc = src.slice(1);
1864
+ let tempStart;
1865
+ this.options.extensions.startInline.forEach(function(getStartIndex) {
1866
+ tempStart = getStartIndex.call(this, tempSrc);
1867
+ if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); }
1868
+ });
1869
+ if (startIndex < Infinity && startIndex >= 0) {
1870
+ cutSrc = src.substring(0, startIndex + 1);
1871
+ }
1872
+ }
1873
+ if (token = this.tokenizer.inlineText(cutSrc, inRawBlock, smartypants)) {
1810
1874
  src = src.substring(token.raw.length);
1811
1875
  if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
1812
1876
  prevChar = token.raw.slice(-1);
@@ -2158,11 +2222,22 @@ var Parser_1 = class Parser {
2158
2222
  item,
2159
2223
  checked,
2160
2224
  task,
2161
- checkbox;
2225
+ checkbox,
2226
+ ret;
2162
2227
 
2163
2228
  const l = tokens.length;
2164
2229
  for (i = 0; i < l; i++) {
2165
2230
  token = tokens[i];
2231
+
2232
+ // Run any renderer extensions
2233
+ if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
2234
+ ret = this.options.extensions.renderers[token.type].call(this, token);
2235
+ if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(token.type)) {
2236
+ out += ret || '';
2237
+ continue;
2238
+ }
2239
+ }
2240
+
2166
2241
  switch (token.type) {
2167
2242
  case 'space': {
2168
2243
  continue;
@@ -2280,6 +2355,7 @@ var Parser_1 = class Parser {
2280
2355
  out += top ? this.renderer.paragraph(body) : body;
2281
2356
  continue;
2282
2357
  }
2358
+
2283
2359
  default: {
2284
2360
  const errMsg = 'Token with "' + token.type + '" type was not found.';
2285
2361
  if (this.options.silent) {
@@ -2302,11 +2378,22 @@ var Parser_1 = class Parser {
2302
2378
  renderer = renderer || this.renderer;
2303
2379
  let out = '',
2304
2380
  i,
2305
- token;
2381
+ token,
2382
+ ret;
2306
2383
 
2307
2384
  const l = tokens.length;
2308
2385
  for (i = 0; i < l; i++) {
2309
2386
  token = tokens[i];
2387
+
2388
+ // Run any renderer extensions
2389
+ if (this.options.extensions && this.options.extensions.renderers && this.options.extensions.renderers[token.type]) {
2390
+ ret = this.options.extensions.renderers[token.type].call(this, token);
2391
+ if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {
2392
+ out += ret || '';
2393
+ continue;
2394
+ }
2395
+ }
2396
+
2310
2397
  switch (token.type) {
2311
2398
  case 'escape': {
2312
2399
  out += renderer.text(token.text);
@@ -2506,46 +2593,114 @@ marked.defaults = defaults;
2506
2593
  * Use Extension
2507
2594
  */
2508
2595
 
2509
- marked.use = function(extension) {
2510
- const opts = merge({}, extension);
2511
- if (extension.renderer) {
2512
- const renderer = marked.defaults.renderer || new Renderer();
2513
- for (const prop in extension.renderer) {
2514
- const prevRenderer = renderer[prop];
2515
- renderer[prop] = (...args) => {
2516
- let ret = extension.renderer[prop].apply(renderer, args);
2517
- if (ret === false) {
2518
- ret = prevRenderer.apply(renderer, args);
2519
- }
2520
- return ret;
2521
- };
2596
+ marked.use = function(...args) {
2597
+ const opts = merge({}, ...args);
2598
+ const extensions = marked.defaults.extensions || { renderers: {}, childTokens: {} };
2599
+ let hasExtensions;
2600
+
2601
+ args.forEach((pack) => {
2602
+ // ==-- Parse "addon" extensions --== //
2603
+ if (pack.extensions) {
2604
+ hasExtensions = true;
2605
+ pack.extensions.forEach((ext) => {
2606
+ if (!ext.name) {
2607
+ throw new Error('extension name required');
2608
+ }
2609
+ if (ext.renderer) { // Renderer extensions
2610
+ const prevRenderer = extensions.renderers ? extensions.renderers[ext.name] : null;
2611
+ if (prevRenderer) {
2612
+ // Replace extension with func to run new extension but fall back if false
2613
+ extensions.renderers[ext.name] = function(...args) {
2614
+ let ret = ext.renderer.apply(this, args);
2615
+ if (ret === false) {
2616
+ ret = prevRenderer.apply(this, args);
2617
+ }
2618
+ return ret;
2619
+ };
2620
+ } else {
2621
+ extensions.renderers[ext.name] = ext.renderer;
2622
+ }
2623
+ }
2624
+ if (ext.tokenizer) { // Tokenizer Extensions
2625
+ if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
2626
+ throw new Error("extension level must be 'block' or 'inline'");
2627
+ }
2628
+ if (extensions[ext.level]) {
2629
+ extensions[ext.level].unshift(ext.tokenizer);
2630
+ } else {
2631
+ extensions[ext.level] = [ext.tokenizer];
2632
+ }
2633
+ if (ext.start) { // Function to check for start of token
2634
+ if (ext.level === 'block') {
2635
+ if (extensions.startBlock) {
2636
+ extensions.startBlock.push(ext.start);
2637
+ } else {
2638
+ extensions.startBlock = [ext.start];
2639
+ }
2640
+ } else if (ext.level === 'inline') {
2641
+ if (extensions.startInline) {
2642
+ extensions.startInline.push(ext.start);
2643
+ } else {
2644
+ extensions.startInline = [ext.start];
2645
+ }
2646
+ }
2647
+ }
2648
+ }
2649
+ if (ext.childTokens) { // Child tokens to be visited by walkTokens
2650
+ extensions.childTokens[ext.name] = ext.childTokens;
2651
+ }
2652
+ });
2522
2653
  }
2523
- opts.renderer = renderer;
2524
- }
2525
- if (extension.tokenizer) {
2526
- const tokenizer = marked.defaults.tokenizer || new Tokenizer();
2527
- for (const prop in extension.tokenizer) {
2528
- const prevTokenizer = tokenizer[prop];
2529
- tokenizer[prop] = (...args) => {
2530
- let ret = extension.tokenizer[prop].apply(tokenizer, args);
2531
- if (ret === false) {
2532
- ret = prevTokenizer.apply(tokenizer, args);
2654
+
2655
+ // ==-- Parse "overwrite" extensions --== //
2656
+ if (pack.renderer) {
2657
+ const renderer = marked.defaults.renderer || new Renderer();
2658
+ for (const prop in pack.renderer) {
2659
+ const prevRenderer = renderer[prop];
2660
+ // Replace renderer with func to run extension, but fall back if false
2661
+ renderer[prop] = (...args) => {
2662
+ let ret = pack.renderer[prop].apply(renderer, args);
2663
+ if (ret === false) {
2664
+ ret = prevRenderer.apply(renderer, args);
2665
+ }
2666
+ return ret;
2667
+ };
2668
+ }
2669
+ opts.renderer = renderer;
2670
+ }
2671
+ if (pack.tokenizer) {
2672
+ const tokenizer = marked.defaults.tokenizer || new Tokenizer();
2673
+ for (const prop in pack.tokenizer) {
2674
+ const prevTokenizer = tokenizer[prop];
2675
+ // Replace tokenizer with func to run extension, but fall back if false
2676
+ tokenizer[prop] = (...args) => {
2677
+ let ret = pack.tokenizer[prop].apply(tokenizer, args);
2678
+ if (ret === false) {
2679
+ ret = prevTokenizer.apply(tokenizer, args);
2680
+ }
2681
+ return ret;
2682
+ };
2683
+ }
2684
+ opts.tokenizer = tokenizer;
2685
+ }
2686
+
2687
+ // ==-- Parse WalkTokens extensions --== //
2688
+ if (pack.walkTokens) {
2689
+ const walkTokens = marked.defaults.walkTokens;
2690
+ opts.walkTokens = (token) => {
2691
+ pack.walkTokens.call(this, token);
2692
+ if (walkTokens) {
2693
+ walkTokens(token);
2533
2694
  }
2534
- return ret;
2535
2695
  };
2536
2696
  }
2537
- opts.tokenizer = tokenizer;
2538
- }
2539
- if (extension.walkTokens) {
2540
- const walkTokens = marked.defaults.walkTokens;
2541
- opts.walkTokens = (token) => {
2542
- extension.walkTokens(token);
2543
- if (walkTokens) {
2544
- walkTokens(token);
2545
- }
2546
- };
2547
- }
2548
- marked.setOptions(opts);
2697
+
2698
+ if (hasExtensions) {
2699
+ opts.extensions = extensions;
2700
+ }
2701
+
2702
+ marked.setOptions(opts);
2703
+ });
2549
2704
  };
2550
2705
 
2551
2706
  /**
@@ -2572,7 +2727,11 @@ marked.walkTokens = function(tokens, callback) {
2572
2727
  break;
2573
2728
  }
2574
2729
  default: {
2575
- if (token.tokens) {
2730
+ if (marked.defaults.extensions && marked.defaults.extensions.childTokens && marked.defaults.extensions.childTokens[token.type]) { // Walk any extensions
2731
+ marked.defaults.extensions.childTokens[token.type].forEach(function(childTokens) {
2732
+ marked.walkTokens(token[childTokens], callback);
2733
+ });
2734
+ } else if (token.tokens) {
2576
2735
  marked.walkTokens(token.tokens, callback);
2577
2736
  }
2578
2737
  }