marked 2.0.4 → 2.1.0

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*$)/,
@@ -1325,7 +1327,7 @@ inline$1.gfm = merge$1({}, inline$1.normal, {
1325
1327
  url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
1326
1328
  _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
1327
1329
  del: /^(~~?)(?=[^\s~])([\s\S]*?[^\s~])\1(?=[^~]|$)/,
1328
- text: /^([`~]+|[^`~])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
1330
+ text: /^([`~]+|[^`~])(?:(?= {2,}\n)|(?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)|[\s\S]*?(?:(?=[\\<!\[`*~_]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@)))/
1329
1331
  });
1330
1332
 
1331
1333
  inline$1.gfm.url = edit(inline$1.gfm.url, 'i')
@@ -1473,9 +1475,21 @@ 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?.block
1482
+ && this.options.extensions.block.some((extTokenizer) => {
1483
+ if (token = extTokenizer.call(this, src, tokens)) {
1484
+ src = src.substring(token.raw.length);
1485
+ tokens.push(token);
1486
+ return true;
1487
+ }
1488
+ return false;
1489
+ })) {
1490
+ continue;
1491
+ }
1492
+
1479
1493
  // newline
1480
1494
  if (token = this.tokenizer.space(src)) {
1481
1495
  src = src.substring(token.raw.length);
@@ -1580,9 +1594,30 @@ var Lexer_1 = class Lexer {
1580
1594
  }
1581
1595
 
1582
1596
  // top-level paragraph
1583
- if (top && (token = this.tokenizer.paragraph(src))) {
1597
+ // prevent paragraph consuming extensions by clipping 'src' to extension start
1598
+ cutSrc = src;
1599
+ if (this.options.extensions?.startBlock) {
1600
+ let startIndex = Infinity;
1601
+ const tempSrc = src.slice(1);
1602
+ let tempStart;
1603
+ this.options.extensions.startBlock.forEach(function(getStartIndex) {
1604
+ tempStart = getStartIndex.call(this, tempSrc);
1605
+ if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); }
1606
+ });
1607
+ if (startIndex < Infinity && startIndex >= 0) {
1608
+ cutSrc = src.substring(0, startIndex + 1);
1609
+ }
1610
+ }
1611
+ if (top && (token = this.tokenizer.paragraph(cutSrc))) {
1612
+ lastToken = tokens[tokens.length - 1];
1613
+ if (lastParagraphClipped && lastToken.type === 'paragraph') {
1614
+ lastToken.raw += '\n' + token.raw;
1615
+ lastToken.text += '\n' + token.text;
1616
+ } else {
1617
+ tokens.push(token);
1618
+ }
1619
+ lastParagraphClipped = (cutSrc.length !== src.length);
1584
1620
  src = src.substring(token.raw.length);
1585
- tokens.push(token);
1586
1621
  continue;
1587
1622
  }
1588
1623
 
@@ -1679,7 +1714,7 @@ var Lexer_1 = class Lexer {
1679
1714
  * Lexing/Compiling
1680
1715
  */
1681
1716
  inlineTokens(src, tokens = [], inLink = false, inRawBlock = false) {
1682
- let token, lastToken;
1717
+ let token, lastToken, cutSrc;
1683
1718
 
1684
1719
  // String with links masked to avoid interference with em and strong
1685
1720
  let maskedSrc = src;
@@ -1713,6 +1748,19 @@ var Lexer_1 = class Lexer {
1713
1748
  }
1714
1749
  keepPrevChar = false;
1715
1750
 
1751
+ // extensions
1752
+ if (this.options?.extensions?.inline
1753
+ && this.options.extensions.inline.some((extTokenizer) => {
1754
+ if (token = extTokenizer.call(this, src, tokens)) {
1755
+ src = src.substring(token.raw.length);
1756
+ tokens.push(token);
1757
+ return true;
1758
+ }
1759
+ return false;
1760
+ })) {
1761
+ continue;
1762
+ }
1763
+
1716
1764
  // escape
1717
1765
  if (token = this.tokenizer.escape(src)) {
1718
1766
  src = src.substring(token.raw.length);
@@ -1725,7 +1773,7 @@ var Lexer_1 = class Lexer {
1725
1773
  src = src.substring(token.raw.length);
1726
1774
  inLink = token.inLink;
1727
1775
  inRawBlock = token.inRawBlock;
1728
- const lastToken = tokens[tokens.length - 1];
1776
+ lastToken = tokens[tokens.length - 1];
1729
1777
  if (lastToken && token.type === 'text' && lastToken.type === 'text') {
1730
1778
  lastToken.raw += token.raw;
1731
1779
  lastToken.text += token.text;
@@ -1748,7 +1796,7 @@ var Lexer_1 = class Lexer {
1748
1796
  // reflink, nolink
1749
1797
  if (token = this.tokenizer.reflink(src, this.tokens.links)) {
1750
1798
  src = src.substring(token.raw.length);
1751
- const lastToken = tokens[tokens.length - 1];
1799
+ lastToken = tokens[tokens.length - 1];
1752
1800
  if (token.type === 'link') {
1753
1801
  token.tokens = this.inlineTokens(token.text, [], true, inRawBlock);
1754
1802
  tokens.push(token);
@@ -1806,7 +1854,21 @@ var Lexer_1 = class Lexer {
1806
1854
  }
1807
1855
 
1808
1856
  // text
1809
- if (token = this.tokenizer.inlineText(src, inRawBlock, smartypants)) {
1857
+ // prevent inlineText consuming extensions by clipping 'src' to extension start
1858
+ cutSrc = src;
1859
+ if (this.options.extensions?.startInline) {
1860
+ let startIndex = Infinity;
1861
+ const tempSrc = src.slice(1);
1862
+ let tempStart;
1863
+ this.options.extensions.startInline.forEach(function(getStartIndex) {
1864
+ tempStart = getStartIndex.call(this, tempSrc);
1865
+ if (typeof tempStart === 'number' && tempStart >= 0) { startIndex = Math.min(startIndex, tempStart); }
1866
+ });
1867
+ if (startIndex < Infinity && startIndex >= 0) {
1868
+ cutSrc = src.substring(0, startIndex + 1);
1869
+ }
1870
+ }
1871
+ if (token = this.tokenizer.inlineText(cutSrc, inRawBlock, smartypants)) {
1810
1872
  src = src.substring(token.raw.length);
1811
1873
  if (token.raw.slice(-1) !== '_') { // Track prevChar before string of ____ started
1812
1874
  prevChar = token.raw.slice(-1);
@@ -2158,11 +2220,22 @@ var Parser_1 = class Parser {
2158
2220
  item,
2159
2221
  checked,
2160
2222
  task,
2161
- checkbox;
2223
+ checkbox,
2224
+ ret;
2162
2225
 
2163
2226
  const l = tokens.length;
2164
2227
  for (i = 0; i < l; i++) {
2165
2228
  token = tokens[i];
2229
+
2230
+ // Run any renderer extensions
2231
+ if (this.options.extensions?.renderers?.[token.type]) {
2232
+ ret = this.options.extensions.renderers[token.type].call(this, token);
2233
+ if (ret !== false || !['space', 'hr', 'heading', 'code', 'table', 'blockquote', 'list', 'html', 'paragraph', 'text'].includes(token.type)) {
2234
+ out += ret || '';
2235
+ continue;
2236
+ }
2237
+ }
2238
+
2166
2239
  switch (token.type) {
2167
2240
  case 'space': {
2168
2241
  continue;
@@ -2280,6 +2353,7 @@ var Parser_1 = class Parser {
2280
2353
  out += top ? this.renderer.paragraph(body) : body;
2281
2354
  continue;
2282
2355
  }
2356
+
2283
2357
  default: {
2284
2358
  const errMsg = 'Token with "' + token.type + '" type was not found.';
2285
2359
  if (this.options.silent) {
@@ -2302,11 +2376,22 @@ var Parser_1 = class Parser {
2302
2376
  renderer = renderer || this.renderer;
2303
2377
  let out = '',
2304
2378
  i,
2305
- token;
2379
+ token,
2380
+ ret;
2306
2381
 
2307
2382
  const l = tokens.length;
2308
2383
  for (i = 0; i < l; i++) {
2309
2384
  token = tokens[i];
2385
+
2386
+ // Run any renderer extensions
2387
+ if (this.options.extensions?.renderers?.[token.type]) {
2388
+ ret = this.options.extensions.renderers[token.type].call(this, token);
2389
+ if (ret !== false || !['escape', 'html', 'link', 'image', 'strong', 'em', 'codespan', 'br', 'del', 'text'].includes(token.type)) {
2390
+ out += ret || '';
2391
+ continue;
2392
+ }
2393
+ }
2394
+
2310
2395
  switch (token.type) {
2311
2396
  case 'escape': {
2312
2397
  out += renderer.text(token.text);
@@ -2416,6 +2501,9 @@ function marked(src, opt, callback) {
2416
2501
 
2417
2502
  if (!err) {
2418
2503
  try {
2504
+ if (opt.walkTokens) {
2505
+ marked.walkTokens(tokens, opt.walkTokens);
2506
+ }
2419
2507
  out = Parser.parse(tokens, opt);
2420
2508
  } catch (e) {
2421
2509
  err = e;
@@ -2503,46 +2591,114 @@ marked.defaults = defaults;
2503
2591
  * Use Extension
2504
2592
  */
2505
2593
 
2506
- marked.use = function(extension) {
2507
- const opts = merge({}, extension);
2508
- if (extension.renderer) {
2509
- const renderer = marked.defaults.renderer || new Renderer();
2510
- for (const prop in extension.renderer) {
2511
- const prevRenderer = renderer[prop];
2512
- renderer[prop] = (...args) => {
2513
- let ret = extension.renderer[prop].apply(renderer, args);
2514
- if (ret === false) {
2515
- ret = prevRenderer.apply(renderer, args);
2516
- }
2517
- return ret;
2518
- };
2594
+ marked.use = function(...args) {
2595
+ const opts = merge({}, ...args);
2596
+ const extensions = marked.defaults.extensions || { renderers: {}, childTokens: {} };
2597
+ let hasExtensions;
2598
+
2599
+ args.forEach((pack) => {
2600
+ // ==-- Parse "addon" extensions --== //
2601
+ if (pack.extensions) {
2602
+ hasExtensions = true;
2603
+ pack.extensions.forEach((ext) => {
2604
+ if (!ext.name) {
2605
+ throw new Error('extension name required');
2606
+ }
2607
+ if (ext.renderer) { // Renderer extensions
2608
+ const prevRenderer = extensions.renderers?.[ext.name];
2609
+ if (prevRenderer) {
2610
+ // Replace extension with func to run new extension but fall back if false
2611
+ extensions.renderers[ext.name] = function(...args) {
2612
+ let ret = ext.renderer.apply(this, args);
2613
+ if (ret === false) {
2614
+ ret = prevRenderer.apply(this, args);
2615
+ }
2616
+ return ret;
2617
+ };
2618
+ } else {
2619
+ extensions.renderers[ext.name] = ext.renderer;
2620
+ }
2621
+ }
2622
+ if (ext.tokenizer) { // Tokenizer Extensions
2623
+ if (!ext.level || (ext.level !== 'block' && ext.level !== 'inline')) {
2624
+ throw new Error("extension level must be 'block' or 'inline'");
2625
+ }
2626
+ if (extensions[ext.level]) {
2627
+ extensions[ext.level].unshift(ext.tokenizer);
2628
+ } else {
2629
+ extensions[ext.level] = [ext.tokenizer];
2630
+ }
2631
+ if (ext.start) { // Function to check for start of token
2632
+ if (ext.level === 'block') {
2633
+ if (extensions.startBlock) {
2634
+ extensions.startBlock.push(ext.start);
2635
+ } else {
2636
+ extensions.startBlock = [ext.start];
2637
+ }
2638
+ } else if (ext.level === 'inline') {
2639
+ if (extensions.startInline) {
2640
+ extensions.startInline.push(ext.start);
2641
+ } else {
2642
+ extensions.startInline = [ext.start];
2643
+ }
2644
+ }
2645
+ }
2646
+ }
2647
+ if (ext.childTokens) { // Child tokens to be visited by walkTokens
2648
+ extensions.childTokens[ext.name] = ext.childTokens;
2649
+ }
2650
+ });
2519
2651
  }
2520
- opts.renderer = renderer;
2521
- }
2522
- if (extension.tokenizer) {
2523
- const tokenizer = marked.defaults.tokenizer || new Tokenizer();
2524
- for (const prop in extension.tokenizer) {
2525
- const prevTokenizer = tokenizer[prop];
2526
- tokenizer[prop] = (...args) => {
2527
- let ret = extension.tokenizer[prop].apply(tokenizer, args);
2528
- if (ret === false) {
2529
- ret = prevTokenizer.apply(tokenizer, args);
2652
+
2653
+ // ==-- Parse "overwrite" extensions --== //
2654
+ if (pack.renderer) {
2655
+ const renderer = marked.defaults.renderer || new Renderer();
2656
+ for (const prop in pack.renderer) {
2657
+ const prevRenderer = renderer[prop];
2658
+ // Replace renderer with func to run extension, but fall back if false
2659
+ renderer[prop] = (...args) => {
2660
+ let ret = pack.renderer[prop].apply(renderer, args);
2661
+ if (ret === false) {
2662
+ ret = prevRenderer.apply(renderer, args);
2663
+ }
2664
+ return ret;
2665
+ };
2666
+ }
2667
+ opts.renderer = renderer;
2668
+ }
2669
+ if (pack.tokenizer) {
2670
+ const tokenizer = marked.defaults.tokenizer || new Tokenizer();
2671
+ for (const prop in pack.tokenizer) {
2672
+ const prevTokenizer = tokenizer[prop];
2673
+ // Replace tokenizer with func to run extension, but fall back if false
2674
+ tokenizer[prop] = (...args) => {
2675
+ let ret = pack.tokenizer[prop].apply(tokenizer, args);
2676
+ if (ret === false) {
2677
+ ret = prevTokenizer.apply(tokenizer, args);
2678
+ }
2679
+ return ret;
2680
+ };
2681
+ }
2682
+ opts.tokenizer = tokenizer;
2683
+ }
2684
+
2685
+ // ==-- Parse WalkTokens extensions --== //
2686
+ if (pack.walkTokens) {
2687
+ const walkTokens = marked.defaults.walkTokens;
2688
+ opts.walkTokens = (token) => {
2689
+ pack.walkTokens.call(this, token);
2690
+ if (walkTokens) {
2691
+ walkTokens(token);
2530
2692
  }
2531
- return ret;
2532
2693
  };
2533
2694
  }
2534
- opts.tokenizer = tokenizer;
2535
- }
2536
- if (extension.walkTokens) {
2537
- const walkTokens = marked.defaults.walkTokens;
2538
- opts.walkTokens = (token) => {
2539
- extension.walkTokens(token);
2540
- if (walkTokens) {
2541
- walkTokens(token);
2542
- }
2543
- };
2544
- }
2545
- marked.setOptions(opts);
2695
+
2696
+ if (hasExtensions) {
2697
+ opts.extensions = extensions;
2698
+ }
2699
+
2700
+ marked.setOptions(opts);
2701
+ });
2546
2702
  };
2547
2703
 
2548
2704
  /**
@@ -2569,7 +2725,12 @@ marked.walkTokens = function(tokens, callback) {
2569
2725
  break;
2570
2726
  }
2571
2727
  default: {
2572
- if (token.tokens) {
2728
+ if (marked.defaults?.extensions?.childTokens?.[token.type]) { // Walk any extensions
2729
+ marked.defaults?.extensions.childTokens[token.type].forEach(function(childTokens) {
2730
+ marked.walkTokens(token[childTokens], callback);
2731
+ });
2732
+ }
2733
+ if (token.tokens && !marked.defaults?.extensions?.childTokens[token.type]) {
2573
2734
  marked.walkTokens(token.tokens, callback);
2574
2735
  }
2575
2736
  }