mirror of
https://github.com/danbulant/markdown-wasm
synced 2026-06-12 19:20:42 +00:00
154 lines
4.8 KiB
JavaScript
154 lines
4.8 KiB
JavaScript
import {
|
|
utf8,
|
|
withTmpBytePtr,
|
|
withOutPtr,
|
|
werrCheck,
|
|
mallocbuf,
|
|
} from "./wlib"
|
|
|
|
export const ready = Module.ready
|
|
|
|
// console.time('wasm load')
|
|
// Module.postRun.push(() => {
|
|
// console.timeEnd('wasm load')
|
|
// })
|
|
|
|
export const ParseFlags = {
|
|
COLLAPSE_WHITESPACE: 0x0001, // In TEXT, collapse non-trivial whitespace into single ' '
|
|
PERMISSIVE_ATX_HEADERS: 0x0002, // Do not require space in ATX headers ( ###header )
|
|
PERMISSIVE_URL_AUTO_LINKS: 0x0004, // Recognize URLs as links even without <...>
|
|
PERMISSIVE_EMAIL_AUTO_LINKS: 0x0008, // Recognize e-mails as links even without <...>
|
|
NO_INDENTED_CODE_BLOCKS: 0x0010, // Disable indented code blocks. (Only fenced code works)
|
|
NO_HTML_BLOCKS: 0x0020, // Disable raw HTML blocks.
|
|
NO_HTML_SPANS: 0x0040, // Disable raw HTML (inline).
|
|
TABLES: 0x0100, // Enable tables extension.
|
|
STRIKETHROUGH: 0x0200, // Enable strikethrough extension.
|
|
PERMISSIVE_WWW_AUTOLINKS: 0x0400, // Enable WWW autolinks (without proto; just 'www.')
|
|
TASK_LISTS: 0x0800, // Enable task list extension.
|
|
LATEX_MATH_SPANS: 0x1000, // Enable $ and $$ containing LaTeX equations.
|
|
WIKI_LINKS: 0x2000, // Enable wiki links extension.
|
|
UNDERLINE: 0x4000, // Enable underline extension (disables '_' for emphasis)
|
|
|
|
// Github style default flags
|
|
DEFAULT: 0x0001 | 0x0002 | 0x0004 | 0x0200 | 0x0100 | 0x0800,
|
|
// COLLAPSE_WHITESPACE
|
|
// PERMISSIVE_ATX_HEADERS
|
|
// PERMISSIVE_URL_AUTO_LINKS
|
|
// STRIKETHROUGH
|
|
// TABLES
|
|
// TASK_LISTS
|
|
|
|
NO_HTML: 0x0020 | 0x0040, // NO_HTML_BLOCKS | NO_HTML_SPANS
|
|
}
|
|
|
|
// these should be in sync with "OutputFlags" in common.h
|
|
const OutputFlags = {
|
|
HTML: 1 << 0, // Output HTML
|
|
XHTML: 1 << 1, // Output XHTML (only has effect with HTML flag set)
|
|
AllowJSURI: 1 << 2, // Allow "javascript:" URIs
|
|
}
|
|
|
|
|
|
export function parse(source, options) {
|
|
options = options || {}
|
|
|
|
let parseFlags = (
|
|
options.parseFlags === undefined ? ParseFlags.DEFAULT :
|
|
options.parseFlags
|
|
)
|
|
|
|
let outputFlags = options.allowJSURIs ? OutputFlags.AllowJSURI : 0
|
|
|
|
switch (options.format) {
|
|
case "xhtml":
|
|
outputFlags |= OutputFlags.HTML | OutputFlags.XHTML
|
|
break
|
|
|
|
case "html":
|
|
case undefined:
|
|
case null:
|
|
case "":
|
|
outputFlags |= OutputFlags.HTML
|
|
break
|
|
|
|
default:
|
|
throw new Error(`invalid format "${options.format}"`)
|
|
}
|
|
|
|
let onCodeBlockPtr = options.onCodeBlock ? create_onCodeBlock_fn(options.onCodeBlock) : 0
|
|
|
|
let buf = as_byte_array(source)
|
|
let outbuf = withOutPtr(outptr => withTmpBytePtr(buf, (inptr, inlen) =>
|
|
_parseUTF8(inptr, inlen, parseFlags, outputFlags, outptr, onCodeBlockPtr)
|
|
))
|
|
|
|
if (options.onCodeBlock)
|
|
removeFunction(onCodeBlockPtr)
|
|
|
|
// check for error and throw if needed
|
|
werrCheck()
|
|
|
|
// DEBUG
|
|
// if (outbuf) {
|
|
// console.log(utf8.decode(outbuf))
|
|
// }
|
|
|
|
if (options.bytes || options.asMemoryView)
|
|
return outbuf
|
|
|
|
return utf8.decode(outbuf)
|
|
}
|
|
|
|
|
|
function create_onCodeBlock_fn(onCodeBlock) {
|
|
// See https://emscripten.org/docs/porting/connecting_cpp_and_javascript/
|
|
// Interacting-with-code.html#calling-javascript-functions-as-function-pointers-from-c
|
|
//
|
|
// Function's C type: JSTextFilterFun
|
|
// (metaptr ptr, metalen ptr, inptr ptr, inlen ptr, outptr ptr) -> outlen int
|
|
const fnptr = addFunction(function(metaptr, metalen, inptr, inlen, outptr) {
|
|
try {
|
|
// lang is the "language" tag, if any, provided with the code block
|
|
const lang = metalen > 0 ? utf8.decode(HEAPU8.subarray(metaptr, metaptr + metalen)) : ""
|
|
|
|
// body is a view into heap memory of the segment of source (UTF8 bytes)
|
|
const body = HEAPU8.subarray(inptr, inptr + inlen)
|
|
let bodystr = undefined
|
|
body.toString = () => (bodystr || (bodystr = utf8.decode(body)))
|
|
|
|
// result is the result from the onCodeBlock function
|
|
let result = null
|
|
result = onCodeBlock(lang, body)
|
|
|
|
if (result === null || result === undefined) {
|
|
// Callback indicates that it does not wish to filter.
|
|
// The md.c implementation will html-encode the body.
|
|
return -1
|
|
}
|
|
|
|
let resbuf = as_byte_array(result)
|
|
if (resbuf.length > 0) {
|
|
// copy resbuf to WASM heap memory
|
|
const resptr = mallocbuf(resbuf, resbuf.length)
|
|
// write pointer value
|
|
HEAPU32[outptr >> 2 /* == outptr / 4 */] = resptr
|
|
// Note: fmt_html.c calls free(resptr)
|
|
}
|
|
|
|
return resbuf.length
|
|
} catch (err) {
|
|
console.error(`error in markdown onCodeBlock callback: ${err.stack||err}`)
|
|
return -1
|
|
}
|
|
}, "iiiiii")
|
|
return fnptr
|
|
}
|
|
|
|
|
|
function as_byte_array(something) {
|
|
if (typeof something == "string")
|
|
return utf8.encode(something)
|
|
if (something instanceof Uint8Array)
|
|
return something
|
|
return new Uint8Array(something)
|
|
}
|