marked.js (6029B)
1 const Lexer = require('./Lexer.js'); 2 const Parser = require('./Parser.js'); 3 const Tokenizer = require('./Tokenizer.js'); 4 const Renderer = require('./Renderer.js'); 5 const TextRenderer = require('./TextRenderer.js'); 6 const Slugger = require('./Slugger.js'); 7 const { 8 merge, 9 checkSanitizeDeprecation, 10 escape 11 } = require('./helpers.js'); 12 const { 13 getDefaults, 14 changeDefaults, 15 defaults 16 } = require('./defaults.js'); 17 18 /** 19 * Marked 20 */ 21 function marked(src, opt, callback) { 22 // throw error in case of non string input 23 if (typeof src === 'undefined' || src === null) { 24 throw new Error('marked(): input parameter is undefined or null'); 25 } 26 if (typeof src !== 'string') { 27 throw new Error('marked(): input parameter is of type ' 28 + Object.prototype.toString.call(src) + ', string expected'); 29 } 30 31 if (typeof opt === 'function') { 32 callback = opt; 33 opt = null; 34 } 35 36 opt = merge({}, marked.defaults, opt || {}); 37 checkSanitizeDeprecation(opt); 38 39 if (callback) { 40 const highlight = opt.highlight; 41 let tokens; 42 43 try { 44 tokens = Lexer.lex(src, opt); 45 } catch (e) { 46 return callback(e); 47 } 48 49 const done = function(err) { 50 let out; 51 52 if (!err) { 53 try { 54 out = Parser.parse(tokens, opt); 55 } catch (e) { 56 err = e; 57 } 58 } 59 60 opt.highlight = highlight; 61 62 return err 63 ? callback(err) 64 : callback(null, out); 65 }; 66 67 if (!highlight || highlight.length < 3) { 68 return done(); 69 } 70 71 delete opt.highlight; 72 73 if (!tokens.length) return done(); 74 75 let pending = 0; 76 marked.walkTokens(tokens, function(token) { 77 if (token.type === 'code') { 78 pending++; 79 setTimeout(() => { 80 highlight(token.text, token.lang, function(err, code) { 81 if (err) { 82 return done(err); 83 } 84 if (code != null && code !== token.text) { 85 token.text = code; 86 token.escaped = true; 87 } 88 89 pending--; 90 if (pending === 0) { 91 done(); 92 } 93 }); 94 }, 0); 95 } 96 }); 97 98 if (pending === 0) { 99 done(); 100 } 101 102 return; 103 } 104 105 try { 106 const tokens = Lexer.lex(src, opt); 107 if (opt.walkTokens) { 108 marked.walkTokens(tokens, opt.walkTokens); 109 } 110 return Parser.parse(tokens, opt); 111 } catch (e) { 112 e.message += '\nPlease report this to https://github.com/markedjs/marked.'; 113 if (opt.silent) { 114 return '<p>An error occurred:</p><pre>' 115 + escape(e.message + '', true) 116 + '</pre>'; 117 } 118 throw e; 119 } 120 } 121 122 /** 123 * Options 124 */ 125 126 marked.options = 127 marked.setOptions = function(opt) { 128 merge(marked.defaults, opt); 129 changeDefaults(marked.defaults); 130 return marked; 131 }; 132 133 marked.getDefaults = getDefaults; 134 135 marked.defaults = defaults; 136 137 /** 138 * Use Extension 139 */ 140 141 marked.use = function(extension) { 142 const opts = merge({}, extension); 143 if (extension.renderer) { 144 const renderer = marked.defaults.renderer || new Renderer(); 145 for (const prop in extension.renderer) { 146 const prevRenderer = renderer[prop]; 147 renderer[prop] = (...args) => { 148 let ret = extension.renderer[prop].apply(renderer, args); 149 if (ret === false) { 150 ret = prevRenderer.apply(renderer, args); 151 } 152 return ret; 153 }; 154 } 155 opts.renderer = renderer; 156 } 157 if (extension.tokenizer) { 158 const tokenizer = marked.defaults.tokenizer || new Tokenizer(); 159 for (const prop in extension.tokenizer) { 160 const prevTokenizer = tokenizer[prop]; 161 tokenizer[prop] = (...args) => { 162 let ret = extension.tokenizer[prop].apply(tokenizer, args); 163 if (ret === false) { 164 ret = prevTokenizer.apply(tokenizer, args); 165 } 166 return ret; 167 }; 168 } 169 opts.tokenizer = tokenizer; 170 } 171 if (extension.walkTokens) { 172 const walkTokens = marked.defaults.walkTokens; 173 opts.walkTokens = (token) => { 174 extension.walkTokens(token); 175 if (walkTokens) { 176 walkTokens(token); 177 } 178 }; 179 } 180 marked.setOptions(opts); 181 }; 182 183 /** 184 * Run callback for every token 185 */ 186 187 marked.walkTokens = function(tokens, callback) { 188 for (const token of tokens) { 189 callback(token); 190 switch (token.type) { 191 case 'table': { 192 for (const cell of token.tokens.header) { 193 marked.walkTokens(cell, callback); 194 } 195 for (const row of token.tokens.cells) { 196 for (const cell of row) { 197 marked.walkTokens(cell, callback); 198 } 199 } 200 break; 201 } 202 case 'list': { 203 marked.walkTokens(token.items, callback); 204 break; 205 } 206 default: { 207 if (token.tokens) { 208 marked.walkTokens(token.tokens, callback); 209 } 210 } 211 } 212 } 213 }; 214 215 /** 216 * Parse Inline 217 */ 218 marked.parseInline = function(src, opt) { 219 // throw error in case of non string input 220 if (typeof src === 'undefined' || src === null) { 221 throw new Error('marked.parseInline(): input parameter is undefined or null'); 222 } 223 if (typeof src !== 'string') { 224 throw new Error('marked.parseInline(): input parameter is of type ' 225 + Object.prototype.toString.call(src) + ', string expected'); 226 } 227 228 opt = merge({}, marked.defaults, opt || {}); 229 checkSanitizeDeprecation(opt); 230 231 try { 232 const tokens = Lexer.lexInline(src, opt); 233 if (opt.walkTokens) { 234 marked.walkTokens(tokens, opt.walkTokens); 235 } 236 return Parser.parseInline(tokens, opt); 237 } catch (e) { 238 e.message += '\nPlease report this to https://github.com/markedjs/marked.'; 239 if (opt.silent) { 240 return '<p>An error occurred:</p><pre>' 241 + escape(e.message + '', true) 242 + '</pre>'; 243 } 244 throw e; 245 } 246 }; 247 248 /** 249 * Expose 250 */ 251 252 marked.Parser = Parser; 253 marked.parser = Parser.parse; 254 255 marked.Renderer = Renderer; 256 marked.TextRenderer = TextRenderer; 257 258 marked.Lexer = Lexer; 259 marked.lexer = Lexer.lex; 260 261 marked.Tokenizer = Tokenizer; 262 263 marked.Slugger = Slugger; 264 265 marked.parse = marked; 266 267 module.exports = marked;