marked 0.8.0 → 1.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/src/marked.js CHANGED
@@ -1,8 +1,8 @@
1
1
  const Lexer = require('./Lexer.js');
2
2
  const Parser = require('./Parser.js');
3
+ const Tokenizer = require('./Tokenizer.js');
3
4
  const Renderer = require('./Renderer.js');
4
5
  const TextRenderer = require('./TextRenderer.js');
5
- const InlineLexer = require('./InlineLexer.js');
6
6
  const Slugger = require('./Slugger.js');
7
7
  const {
8
8
  merge,
@@ -28,18 +28,17 @@ function marked(src, opt, callback) {
28
28
  + Object.prototype.toString.call(src) + ', string expected');
29
29
  }
30
30
 
31
- if (callback || typeof opt === 'function') {
32
- if (!callback) {
33
- callback = opt;
34
- opt = null;
35
- }
31
+ if (typeof opt === 'function') {
32
+ callback = opt;
33
+ opt = null;
34
+ }
35
+
36
+ opt = merge({}, marked.defaults, opt || {});
37
+ checkSanitizeDeprecation(opt);
36
38
 
37
- opt = merge({}, marked.defaults, opt || {});
38
- checkSanitizeDeprecation(opt);
39
+ if (callback) {
39
40
  const highlight = opt.highlight;
40
- let tokens,
41
- pending,
42
- i = 0;
41
+ let tokens;
43
42
 
44
43
  try {
45
44
  tokens = Lexer.lex(src, opt);
@@ -47,20 +46,15 @@ function marked(src, opt, callback) {
47
46
  return callback(e);
48
47
  }
49
48
 
50
- pending = tokens.length;
51
-
52
49
  const done = function(err) {
53
- if (err) {
54
- opt.highlight = highlight;
55
- return callback(err);
56
- }
57
-
58
50
  let out;
59
51
 
60
- try {
61
- out = Parser.parse(tokens, opt);
62
- } catch (e) {
63
- err = e;
52
+ if (!err) {
53
+ try {
54
+ out = Parser.parse(tokens, opt);
55
+ } catch (e) {
56
+ err = e;
57
+ }
64
58
  }
65
59
 
66
60
  opt.highlight = highlight;
@@ -76,34 +70,45 @@ function marked(src, opt, callback) {
76
70
 
77
71
  delete opt.highlight;
78
72
 
79
- if (!pending) return done();
73
+ if (!tokens.length) return done();
80
74
 
81
- for (; i < tokens.length; i++) {
82
- (function(token) {
83
- if (token.type !== 'code') {
84
- return --pending || done();
85
- }
86
- return highlight(token.text, token.lang, function(err, code) {
87
- if (err) return done(err);
88
- if (code == null || code === token.text) {
89
- return --pending || done();
75
+ let pending = 0;
76
+ marked.walkTokens(tokens, function(token) {
77
+ if (token.type === 'code') {
78
+ pending++;
79
+ highlight(token.text, token.lang, function(err, code) {
80
+ if (err) {
81
+ return done(err);
82
+ }
83
+ if (code != null && code !== token.text) {
84
+ token.text = code;
85
+ token.escaped = true;
86
+ }
87
+
88
+ pending--;
89
+ if (pending === 0) {
90
+ done();
90
91
  }
91
- token.text = code;
92
- token.escaped = true;
93
- --pending || done();
94
92
  });
95
- })(tokens[i]);
93
+ }
94
+ });
95
+
96
+ if (pending === 0) {
97
+ done();
96
98
  }
97
99
 
98
100
  return;
99
101
  }
102
+
100
103
  try {
101
- opt = merge({}, marked.defaults, opt || {});
102
- checkSanitizeDeprecation(opt);
103
- return Parser.parse(Lexer.lex(src, opt), opt);
104
+ const tokens = Lexer.lex(src, opt);
105
+ if (opt.walkTokens) {
106
+ marked.walkTokens(tokens, opt.walkTokens);
107
+ }
108
+ return Parser.parse(tokens, opt);
104
109
  } catch (e) {
105
110
  e.message += '\nPlease report this to https://github.com/markedjs/marked.';
106
- if ((opt || marked.defaults).silent) {
111
+ if (opt.silent) {
107
112
  return '<p>An error occurred:</p><pre>'
108
113
  + escape(e.message + '', true)
109
114
  + '</pre>';
@@ -127,6 +132,84 @@ marked.getDefaults = getDefaults;
127
132
 
128
133
  marked.defaults = defaults;
129
134
 
135
+ /**
136
+ * Use Extension
137
+ */
138
+
139
+ marked.use = function(extension) {
140
+ const opts = merge({}, extension);
141
+ if (extension.renderer) {
142
+ const renderer = marked.defaults.renderer || new Renderer();
143
+ for (const prop in extension.renderer) {
144
+ const prevRenderer = renderer[prop];
145
+ renderer[prop] = (...args) => {
146
+ let ret = extension.renderer[prop].apply(renderer, args);
147
+ if (ret === false) {
148
+ ret = prevRenderer.apply(renderer, args);
149
+ }
150
+ return ret;
151
+ };
152
+ }
153
+ opts.renderer = renderer;
154
+ }
155
+ if (extension.tokenizer) {
156
+ const tokenizer = marked.defaults.tokenizer || new Tokenizer();
157
+ for (const prop in extension.tokenizer) {
158
+ const prevTokenizer = tokenizer[prop];
159
+ tokenizer[prop] = (...args) => {
160
+ let ret = extension.tokenizer[prop].apply(tokenizer, args);
161
+ if (ret === false) {
162
+ ret = prevTokenizer.apply(tokenizer, args);
163
+ }
164
+ return ret;
165
+ };
166
+ }
167
+ opts.tokenizer = tokenizer;
168
+ }
169
+ if (extension.walkTokens) {
170
+ const walkTokens = marked.defaults.walkTokens;
171
+ opts.walkTokens = (token) => {
172
+ extension.walkTokens(token);
173
+ if (walkTokens) {
174
+ walkTokens(token);
175
+ }
176
+ };
177
+ }
178
+ marked.setOptions(opts);
179
+ };
180
+
181
+ /**
182
+ * Run callback for every token
183
+ */
184
+
185
+ marked.walkTokens = function(tokens, callback) {
186
+ for (const token of tokens) {
187
+ callback(token);
188
+ switch (token.type) {
189
+ case 'table': {
190
+ for (const cell of token.tokens.header) {
191
+ marked.walkTokens(cell, callback);
192
+ }
193
+ for (const row of token.tokens.cells) {
194
+ for (const cell of row) {
195
+ marked.walkTokens(cell, callback);
196
+ }
197
+ }
198
+ break;
199
+ }
200
+ case 'list': {
201
+ marked.walkTokens(token.items, callback);
202
+ break;
203
+ }
204
+ default: {
205
+ if (token.tokens) {
206
+ marked.walkTokens(token.tokens, callback);
207
+ }
208
+ }
209
+ }
210
+ }
211
+ };
212
+
130
213
  /**
131
214
  * Expose
132
215
  */
@@ -140,8 +223,7 @@ marked.TextRenderer = TextRenderer;
140
223
  marked.Lexer = Lexer;
141
224
  marked.lexer = Lexer.lex;
142
225
 
143
- marked.InlineLexer = InlineLexer;
144
- marked.inlineLexer = InlineLexer.output;
226
+ marked.Tokenizer = Tokenizer;
145
227
 
146
228
  marked.Slugger = Slugger;
147
229
 
package/src/rules.js CHANGED
@@ -10,7 +10,7 @@ const {
10
10
  const block = {
11
11
  newline: /^\n+/,
12
12
  code: /^( {4}[^\n]+\n*)+/,
13
- fences: /^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
13
+ fences: /^ {0,3}(`{3,}(?=[^`\n]*\n)|~{3,})([^\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
14
14
  hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
15
15
  heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
16
16
  blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
@@ -69,10 +69,10 @@ block.html = edit(block.html, 'i')
69
69
 
70
70
  block.paragraph = edit(block._paragraph)
71
71
  .replace('hr', block.hr)
72
- .replace('heading', ' {0,3}#{1,6} +')
72
+ .replace('heading', ' {0,3}#{1,6} ')
73
73
  .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
74
74
  .replace('blockquote', ' {0,3}>')
75
- .replace('fences', ' {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n')
75
+ .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
76
76
  .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
77
77
  .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
78
78
  .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
@@ -93,10 +93,36 @@ block.normal = merge({}, block);
93
93
  */
94
94
 
95
95
  block.gfm = merge({}, block.normal, {
96
- nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,
97
- table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/
96
+ nptable: '^ *([^|\\n ].*\\|.*)\\n' // Header
97
+ + ' *([-:]+ *\\|[-| :]*)' // Align
98
+ + '(?:\\n((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', // Cells
99
+ table: '^ *\\|(.+)\\n' // Header
100
+ + ' *\\|?( *[-:]+[-| :]*)' // Align
101
+ + '(?:\\n *((?:(?!\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)' // Cells
98
102
  });
99
103
 
104
+ block.gfm.nptable = edit(block.gfm.nptable)
105
+ .replace('hr', block.hr)
106
+ .replace('heading', ' {0,3}#{1,6} ')
107
+ .replace('blockquote', ' {0,3}>')
108
+ .replace('code', ' {4}[^\\n]')
109
+ .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
110
+ .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
111
+ .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
112
+ .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
113
+ .getRegex();
114
+
115
+ block.gfm.table = edit(block.gfm.table)
116
+ .replace('hr', block.hr)
117
+ .replace('heading', ' {0,3}#{1,6} ')
118
+ .replace('blockquote', ' {0,3}>')
119
+ .replace('code', ' {4}[^\\n]')
120
+ .replace('fences', ' {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n')
121
+ .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
122
+ .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
123
+ .replace('tag', block._tag) // tables can be interrupted by type (6) html blocks
124
+ .getRegex();
125
+
100
126
  /**
101
127
  * Pedantic grammar (original John Gruber's loose markdown specification)
102
128
  */
@@ -143,7 +169,7 @@ const inline = {
143
169
  reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
144
170
  nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
145
171
  strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
146
- em: /^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
172
+ em: /^_([^\s_])_(?!_)|^_([^\s_<][\s\S]*?[^\s_])_(?!_|[^\s,punctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\s,punctuation])|^\*([^\s*<\[])\*(?!\*)|^\*([^\s<"][\s\S]*?[^\s\[\*])\*(?![\]`punctuation])|^\*([^\s*"<\[][\s\S]*[^\s])\*(?!\*)/,
147
173
  code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
148
174
  br: /^( {2,}|\\)\n(?!\s*$)/,
149
175
  del: noopTest,
@@ -152,7 +178,8 @@ const inline = {
152
178
 
153
179
  // list of punctuation marks from common mark spec
154
180
  // without ` and ] to workaround Rule 17 (inline code blocks/links)
155
- inline._punctuation = '!"#$%&\'()*+,\\-./:;<=>?@\\[^_{|}~';
181
+ // without , to work around example 393
182
+ inline._punctuation = '!"#$%&\'()*+\\-./:;<=>?@\\[^_{|}~';
156
183
  inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
157
184
 
158
185
  inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
@@ -1,293 +0,0 @@
1
- const Renderer = require('./Renderer.js');
2
- const { defaults } = require('./defaults.js');
3
- const { inline } = require('./rules.js');
4
- const {
5
- findClosingBracket,
6
- escape
7
- } = require('./helpers.js');
8
-
9
- /**
10
- * Inline Lexer & Compiler
11
- */
12
- module.exports = class InlineLexer {
13
- constructor(links, options) {
14
- this.options = options || defaults;
15
- this.links = links;
16
- this.rules = inline.normal;
17
- this.options.renderer = this.options.renderer || new Renderer();
18
- this.renderer = this.options.renderer;
19
- this.renderer.options = this.options;
20
-
21
- if (!this.links) {
22
- throw new Error('Tokens array requires a `links` property.');
23
- }
24
-
25
- if (this.options.pedantic) {
26
- this.rules = inline.pedantic;
27
- } else if (this.options.gfm) {
28
- if (this.options.breaks) {
29
- this.rules = inline.breaks;
30
- } else {
31
- this.rules = inline.gfm;
32
- }
33
- }
34
- }
35
-
36
- /**
37
- * Expose Inline Rules
38
- */
39
- static get rules() {
40
- return inline;
41
- }
42
-
43
- /**
44
- * Static Lexing/Compiling Method
45
- */
46
- static output(src, links, options) {
47
- const inline = new InlineLexer(links, options);
48
- return inline.output(src);
49
- }
50
-
51
- /**
52
- * Lexing/Compiling
53
- */
54
- output(src) {
55
- let out = '',
56
- link,
57
- text,
58
- href,
59
- title,
60
- cap,
61
- prevCapZero;
62
-
63
- while (src) {
64
- // escape
65
- if (cap = this.rules.escape.exec(src)) {
66
- src = src.substring(cap[0].length);
67
- out += escape(cap[1]);
68
- continue;
69
- }
70
-
71
- // tag
72
- if (cap = this.rules.tag.exec(src)) {
73
- if (!this.inLink && /^<a /i.test(cap[0])) {
74
- this.inLink = true;
75
- } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
76
- this.inLink = false;
77
- }
78
- if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
79
- this.inRawBlock = true;
80
- } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
81
- this.inRawBlock = false;
82
- }
83
-
84
- src = src.substring(cap[0].length);
85
- out += this.options.sanitize
86
- ? this.options.sanitizer
87
- ? this.options.sanitizer(cap[0])
88
- : escape(cap[0])
89
- : cap[0];
90
- continue;
91
- }
92
-
93
- // link
94
- if (cap = this.rules.link.exec(src)) {
95
- const lastParenIndex = findClosingBracket(cap[2], '()');
96
- if (lastParenIndex > -1) {
97
- const start = cap[0].indexOf('!') === 0 ? 5 : 4;
98
- const linkLen = start + cap[1].length + lastParenIndex;
99
- cap[2] = cap[2].substring(0, lastParenIndex);
100
- cap[0] = cap[0].substring(0, linkLen).trim();
101
- cap[3] = '';
102
- }
103
- src = src.substring(cap[0].length);
104
- this.inLink = true;
105
- href = cap[2];
106
- if (this.options.pedantic) {
107
- link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
108
-
109
- if (link) {
110
- href = link[1];
111
- title = link[3];
112
- } else {
113
- title = '';
114
- }
115
- } else {
116
- title = cap[3] ? cap[3].slice(1, -1) : '';
117
- }
118
- href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
119
- out += this.outputLink(cap, {
120
- href: InlineLexer.escapes(href),
121
- title: InlineLexer.escapes(title)
122
- });
123
- this.inLink = false;
124
- continue;
125
- }
126
-
127
- // reflink, nolink
128
- if ((cap = this.rules.reflink.exec(src))
129
- || (cap = this.rules.nolink.exec(src))) {
130
- src = src.substring(cap[0].length);
131
- link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
132
- link = this.links[link.toLowerCase()];
133
- if (!link || !link.href) {
134
- out += cap[0].charAt(0);
135
- src = cap[0].substring(1) + src;
136
- continue;
137
- }
138
- this.inLink = true;
139
- out += this.outputLink(cap, link);
140
- this.inLink = false;
141
- continue;
142
- }
143
-
144
- // strong
145
- if (cap = this.rules.strong.exec(src)) {
146
- src = src.substring(cap[0].length);
147
- out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
148
- continue;
149
- }
150
-
151
- // em
152
- if (cap = this.rules.em.exec(src)) {
153
- src = src.substring(cap[0].length);
154
- out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
155
- continue;
156
- }
157
-
158
- // code
159
- if (cap = this.rules.code.exec(src)) {
160
- src = src.substring(cap[0].length);
161
- out += this.renderer.codespan(escape(cap[2].trim(), true));
162
- continue;
163
- }
164
-
165
- // br
166
- if (cap = this.rules.br.exec(src)) {
167
- src = src.substring(cap[0].length);
168
- out += this.renderer.br();
169
- continue;
170
- }
171
-
172
- // del (gfm)
173
- if (cap = this.rules.del.exec(src)) {
174
- src = src.substring(cap[0].length);
175
- out += this.renderer.del(this.output(cap[1]));
176
- continue;
177
- }
178
-
179
- // autolink
180
- if (cap = this.rules.autolink.exec(src)) {
181
- src = src.substring(cap[0].length);
182
- if (cap[2] === '@') {
183
- text = escape(this.mangle(cap[1]));
184
- href = 'mailto:' + text;
185
- } else {
186
- text = escape(cap[1]);
187
- href = text;
188
- }
189
- out += this.renderer.link(href, null, text);
190
- continue;
191
- }
192
-
193
- // url (gfm)
194
- if (!this.inLink && (cap = this.rules.url.exec(src))) {
195
- if (cap[2] === '@') {
196
- text = escape(cap[0]);
197
- href = 'mailto:' + text;
198
- } else {
199
- // do extended autolink path validation
200
- do {
201
- prevCapZero = cap[0];
202
- cap[0] = this.rules._backpedal.exec(cap[0])[0];
203
- } while (prevCapZero !== cap[0]);
204
- text = escape(cap[0]);
205
- if (cap[1] === 'www.') {
206
- href = 'http://' + text;
207
- } else {
208
- href = text;
209
- }
210
- }
211
- src = src.substring(cap[0].length);
212
- out += this.renderer.link(href, null, text);
213
- continue;
214
- }
215
-
216
- // text
217
- if (cap = this.rules.text.exec(src)) {
218
- src = src.substring(cap[0].length);
219
- if (this.inRawBlock) {
220
- out += this.renderer.text(this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0]);
221
- } else {
222
- out += this.renderer.text(escape(this.smartypants(cap[0])));
223
- }
224
- continue;
225
- }
226
-
227
- if (src) {
228
- throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
229
- }
230
- }
231
-
232
- return out;
233
- }
234
-
235
- static escapes(text) {
236
- return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
237
- }
238
-
239
- /**
240
- * Compile Link
241
- */
242
- outputLink(cap, link) {
243
- const href = link.href,
244
- title = link.title ? escape(link.title) : null;
245
-
246
- return cap[0].charAt(0) !== '!'
247
- ? this.renderer.link(href, title, this.output(cap[1]))
248
- : this.renderer.image(href, title, escape(cap[1]));
249
- }
250
-
251
- /**
252
- * Smartypants Transformations
253
- */
254
- smartypants(text) {
255
- if (!this.options.smartypants) return text;
256
- return text
257
- // em-dashes
258
- .replace(/---/g, '\u2014')
259
- // en-dashes
260
- .replace(/--/g, '\u2013')
261
- // opening singles
262
- .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
263
- // closing singles & apostrophes
264
- .replace(/'/g, '\u2019')
265
- // opening doubles
266
- .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
267
- // closing doubles
268
- .replace(/"/g, '\u201d')
269
- // ellipses
270
- .replace(/\.{3}/g, '\u2026');
271
- }
272
-
273
- /**
274
- * Mangle Links
275
- */
276
- mangle(text) {
277
- if (!this.options.mangle) return text;
278
- const l = text.length;
279
- let out = '',
280
- i = 0,
281
- ch;
282
-
283
- for (; i < l; i++) {
284
- ch = text.charCodeAt(i);
285
- if (Math.random() > 0.5) {
286
- ch = 'x' + ch.toString(16);
287
- }
288
- out += '&#' + ch + ';';
289
- }
290
-
291
- return out;
292
- }
293
- };