mirror of
https://github.com/danbulant/markdown-wasm
synced 2026-06-14 04:01:08 +00:00
Compare commits
No commits in common. "master" and "v1.1.2" have entirely different histories.
30 changed files with 157 additions and 412 deletions
77
README.md
77
README.md
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
Very fast Markdown parser & HTML renderer implemented in WebAssembly
|
Very fast Markdown parser & HTML renderer implemented in WebAssembly
|
||||||
|
|
||||||
- Zero dependencies (31 kB gzipped)
|
- Zero dependencies
|
||||||
- Portable & safe (WASM executes in isolated memory and can run almost anywhere)
|
- Portable & safe
|
||||||
- [Simple API](#api)
|
- [Simple API](#api)
|
||||||
- [Very fast](#benchmarks)
|
- [Fast and efficient](#benchmarks)
|
||||||
- Based on [md4c](http://github.com/mity/md4c) — compliant to the CommonMark specification
|
- JS + WASM is only 31 kB gzipped
|
||||||
|
|
||||||
|
Based on [md4c](http://github.com/mity/md4c)
|
||||||
|
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
|
|
@ -89,22 +91,29 @@ See [`test/benchmark`](test/benchmark#readme) for more information.
|
||||||
* parse reads markdown source at s and converts it to HTML.
|
* parse reads markdown source at s and converts it to HTML.
|
||||||
* When output is a byte array, it will be a reference.
|
* When output is a byte array, it will be a reference.
|
||||||
*/
|
*/
|
||||||
export function parse(s :Source, o? :ParseOptions & { bytes? :never|false }) :string
|
export function parse(s :Source, o? :ParseOptions & { asMemoryView? :never|false }) :string
|
||||||
export function parse(s :Source, o? :ParseOptions & { bytes :true }) :Uint8Array
|
export function parse(s :Source, o? :ParseOptions & { asMemoryView :true }) :Uint8Array
|
||||||
|
|
||||||
/** Markdown source code can be provided as a JavaScript string or UTF8 encoded data */
|
/** Markdown source code can be provided as a JavaScript string or UTF8 encoded data */
|
||||||
type Source = string | ArrayLike<number>
|
type Source = string|ArrayLike<number>
|
||||||
|
|
||||||
/** Options for the parse function */
|
/** Options for the parse function */
|
||||||
export interface ParseOptions {
|
export interface ParseOptions {
|
||||||
/** Customize parsing. Defaults to ParseFlags.DEFAULT */
|
/**
|
||||||
|
* Customize parsing.
|
||||||
|
* If not provided, the following flags are used, equating to github-style parsing:
|
||||||
|
* COLLAPSE_WHITESPACE
|
||||||
|
* PERMISSIVE_ATX_HEADERS
|
||||||
|
* PERMISSIVE_URL_AUTO_LINKS
|
||||||
|
* STRIKETHROUGH
|
||||||
|
* TABLES
|
||||||
|
* TASK_LISTS
|
||||||
|
*/
|
||||||
parseFlags? :ParseFlags
|
parseFlags? :ParseFlags
|
||||||
|
|
||||||
/** Select output format. Defaults to "html" */
|
|
||||||
format? : "html" | "xhtml"
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* bytes=true causes parse() to return the result as a Uint8Array instead of a string.
|
* asMemoryView=true causes parse() to return a view of heap memory as a Uint8Array,
|
||||||
|
* instead of a string.
|
||||||
*
|
*
|
||||||
* The returned Uint8Array is only valid until the next call to parse().
|
* The returned Uint8Array is only valid until the next call to parse().
|
||||||
* If you need to keep the returned data around, call Uint8Array.slice() to make a copy,
|
* If you need to keep the returned data around, call Uint8Array.slice() to make a copy,
|
||||||
|
|
@ -113,36 +122,9 @@ export interface ParseOptions {
|
||||||
* This only provides a performance benefit when you never need to convert the output
|
* This only provides a performance benefit when you never need to convert the output
|
||||||
* to a string. In most cases you're better off leaving this unset or false.
|
* to a string. In most cases you're better off leaving this unset or false.
|
||||||
*/
|
*/
|
||||||
bytes? :boolean
|
|
||||||
|
|
||||||
/** Allow "javascript:" in links */
|
|
||||||
allowJSURIs? :boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional callback which if provided is called for each code block.
|
|
||||||
* langname holds the "language tag", if any, of the block.
|
|
||||||
*
|
|
||||||
* The returned value is inserted into the resulting HTML verbatim, without HTML escaping.
|
|
||||||
* Thus, you should take care of properly escaping any special HTML characters.
|
|
||||||
*
|
|
||||||
* If the function returns null or undefined, or an exception occurs, the body will be
|
|
||||||
* included as-is after going through HTML escaping.
|
|
||||||
*
|
|
||||||
* Note that use of this callback has an adverse impact on performance as it casues
|
|
||||||
* calls and data to be bridged between WASM and JS on every invocation.
|
|
||||||
*/
|
|
||||||
onCodeBlock? :(langname :string, body :UTF8Bytes) => Uint8Array|string|null|undefined
|
|
||||||
|
|
||||||
/** @depreceated use "bytes" instead (v1.1.1) */
|
|
||||||
asMemoryView? :boolean
|
asMemoryView? :boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/** UTF8Bytes is a Uint8Array representing UTF8 text */
|
|
||||||
export interface UTF8Bytes extends Uint8Array {
|
|
||||||
/** toString returns a UTF8 decoded string (lazily decoded and cached) */
|
|
||||||
toString() :string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Flags that customize Markdown parsing */
|
/** Flags that customize Markdown parsing */
|
||||||
export enum ParseFlags {
|
export enum ParseFlags {
|
||||||
/** In TEXT, collapse non-trivial whitespace into single ' ' */ COLLAPSE_WHITESPACE,
|
/** In TEXT, collapse non-trivial whitespace into single ' ' */ COLLAPSE_WHITESPACE,
|
||||||
|
|
@ -158,24 +140,13 @@ export enum ParseFlags {
|
||||||
/** Enable tables extension. */ TABLES,
|
/** Enable tables extension. */ TABLES,
|
||||||
/** Enable task list extension. */ TASK_LISTS,
|
/** Enable task list extension. */ TASK_LISTS,
|
||||||
/** Enable wiki links extension. */ WIKI_LINKS,
|
/** Enable wiki links extension. */ WIKI_LINKS,
|
||||||
/** Enable underline extension (disables '_' for emphasis) */ UNDERLINE,
|
|
||||||
|
|
||||||
/** Default flags are:
|
/** Default flags */ DEFAULT,
|
||||||
* COLLAPSE_WHITESPACE |
|
/** Shorthand for NO_HTML_BLOCKS | NO_HTML_SPANS */ NO_HTML,
|
||||||
* PERMISSIVE_ATX_HEADERS |
|
|
||||||
* PERMISSIVE_URL_AUTO_LINKS |
|
|
||||||
* STRIKETHROUGH |
|
|
||||||
* TABLES |
|
|
||||||
* TASK_LISTS
|
|
||||||
*/
|
|
||||||
DEFAULT,
|
|
||||||
|
|
||||||
/** Shorthand for NO_HTML_BLOCKS | NO_HTML_SPANS */
|
|
||||||
NO_HTML,
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
See [`markdown.d.ts`](markdown.d.ts)
|
See `markdown.d.ts`
|
||||||
|
|
||||||
|
|
||||||
## Building from source
|
## Building from source
|
||||||
|
|
|
||||||
2
dist/markdown.es.js
vendored
2
dist/markdown.es.js
vendored
File diff suppressed because one or more lines are too long
2
dist/markdown.es.js.map
vendored
2
dist/markdown.es.js.map
vendored
File diff suppressed because one or more lines are too long
2
dist/markdown.js
vendored
2
dist/markdown.js
vendored
File diff suppressed because one or more lines are too long
2
dist/markdown.js.map
vendored
2
dist/markdown.js.map
vendored
File diff suppressed because one or more lines are too long
4
dist/markdown.node.js
vendored
4
dist/markdown.node.js
vendored
File diff suppressed because one or more lines are too long
2
dist/markdown.node.js.map
vendored
2
dist/markdown.node.js.map
vendored
File diff suppressed because one or more lines are too long
BIN
dist/markdown.wasm
vendored
Executable file → Normal file
BIN
dist/markdown.wasm
vendored
Executable file → Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -13,16 +13,16 @@
|
||||||
<p>line 1
|
<p>line 1
|
||||||
line 2</p>
|
line 2</p>
|
||||||
<h2><a id="code-poetry" class="anchor" aria-hidden="true" href="#code-poetry"></a>Code & Poetry</h2>
|
<h2><a id="code-poetry" class="anchor" aria-hidden="true" href="#code-poetry"></a>Code & Poetry</h2>
|
||||||
<pre><code>YOU CAN ALSO INDENT
|
<pre><code>You can also indent
|
||||||
BLOCKS TO DISPLAY
|
blocks to display
|
||||||
CODE OR POETRY.
|
code or poetry.
|
||||||
|
|
||||||
INDENTED CODE/POETRY BLOCKS
|
Indented code/poetry blocks
|
||||||
CAN BE HARD-WRAPPED.
|
can be hard-wrapped.
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<p><b>Or, wrap your code in three backticks:</b></p>
|
<p><b>Or, wrap your code in three backticks:</b></p>
|
||||||
<pre><code class="language-js">FUNCTION CODEBLOCKS() {
|
<pre><code class="language-js">function codeBlocks() {
|
||||||
RETURN "CAN BE INSERTED"
|
return "Can be inserted"
|
||||||
}
|
}
|
||||||
</code></pre>
|
</code></pre>
|
||||||
<h3><a id="block-quotes" class="anchor" aria-hidden="true" href="#block-quotes"></a>Block Quotes</h3>
|
<h3><a id="block-quotes" class="anchor" aria-hidden="true" href="#block-quotes"></a>Block Quotes</h3>
|
||||||
|
|
@ -79,4 +79,3 @@ breaks.</p>
|
||||||
<h2><a id="anot-her" class="anchor" aria-hidden="true" href="#anot-her"></a>Anöt######her!</h2>
|
<h2><a id="anot-her" class="anchor" aria-hidden="true" href="#anot-her"></a>Anöt######her!</h2>
|
||||||
<h2><a id="anot-her" class="anchor" aria-hidden="true" href="#anot-her"></a>?!Anöt//her!!</h2>
|
<h2><a id="anot-her" class="anchor" aria-hidden="true" href="#anot-her"></a>?!Anöt//her!!</h2>
|
||||||
<h2><a id="" class="anchor" aria-hidden="true" href="#"></a>?!!</h2>
|
<h2><a id="" class="anchor" aria-hidden="true" href="#"></a>?!!</h2>
|
||||||
<p><a href="">XSS test</a></p>
|
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,8 @@
|
||||||
const fs = require("fs")
|
const fs = require("fs")
|
||||||
const md = require("../dist/markdown.node.js")
|
const md = require("../dist/markdown.node.js")
|
||||||
// const md = require("../build/debug/markdown.node.js")
|
|
||||||
|
|
||||||
const source = fs.readFileSync(__dirname + "/example.md")
|
const source = fs.readFileSync(__dirname + "/example.md")
|
||||||
const outbuf = md.parse(source, {
|
const outbuf = md.parse(source, { bytes: true })
|
||||||
bytes: true,
|
|
||||||
onCodeBlock(lang, body) {
|
|
||||||
console.log(`onCodeBlock (${lang})`)
|
|
||||||
return html_escape(body.toString().toUpperCase())
|
|
||||||
},
|
|
||||||
})
|
|
||||||
const outfile = __dirname + "/example.html"
|
const outfile = __dirname + "/example.html"
|
||||||
console.log("write", outfile)
|
console.log("write", outfile)
|
||||||
fs.writeFileSync(outfile, outbuf)
|
fs.writeFileSync(outfile, outbuf)
|
||||||
|
|
@ -18,36 +11,16 @@ console.log(fs.readFileSync(outfile, "utf8"))
|
||||||
|
|
||||||
// mini benchmark
|
// mini benchmark
|
||||||
if (process.argv.includes("-bench")) {
|
if (process.argv.includes("-bench")) {
|
||||||
benchmark("bytes", {
|
console.log("benchmark start (sampling ~2s of data)")
|
||||||
bytes: true,
|
|
||||||
})
|
|
||||||
benchmark("bytes + onCodeBlock", {
|
|
||||||
bytes: true,
|
|
||||||
onCodeBlock(lang, body) {
|
|
||||||
return null // causes the body to be HTML-escaped & included as is
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function benchmark(name, options) {
|
|
||||||
console.log(`benchmark start ${name} (sampling ~2s of data)`)
|
|
||||||
const timeStart = Date.now()
|
const timeStart = Date.now()
|
||||||
const N = 1000, T = 2000
|
const N = 1000, T = 2000
|
||||||
let ntotal = 0
|
let ntotal = 0
|
||||||
while (Date.now() - timeStart < 2000) {
|
while (Date.now() - timeStart < 2000) {
|
||||||
for (let i = 0; i < N; i++) {
|
for (let i = 0; i < N; i++) {
|
||||||
global["dont-optimize-away"] = md.parse(source, options)
|
global["dont-optimize-away"] = md.parse(source, { bytes: true })
|
||||||
}
|
}
|
||||||
ntotal += N
|
ntotal += N
|
||||||
}
|
}
|
||||||
const timeSpent = Date.now() - timeStart
|
const timeSpent = Date.now() - timeStart
|
||||||
console.log(
|
console.log(`benchmark end -- avg parse time: ${((timeSpent / ntotal) * 1000).toFixed(1)}us`)
|
||||||
`benchmark end ${name} -- avg parse time: ` +
|
|
||||||
`${((timeSpent / ntotal) * 1000).toFixed(1)}us`)
|
|
||||||
}
|
|
||||||
|
|
||||||
function html_escape(str) {
|
|
||||||
return str.replace(/[&<>'"]/g, tag => ({
|
|
||||||
'&': '&','<': '<','>': '>',"'": ''','"': '"'
|
|
||||||
}[tag]))
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,5 +82,3 @@ function codeBlocks() {
|
||||||
## ?!Anöt//her!!
|
## ?!Anöt//her!!
|
||||||
|
|
||||||
## ?!!
|
## ?!!
|
||||||
|
|
||||||
[XSS test](javAscRipt:alert("xss"))
|
|
||||||
|
|
|
||||||
24
markdown.d.ts
vendored
24
markdown.d.ts
vendored
|
|
@ -28,34 +28,10 @@ export interface ParseOptions {
|
||||||
*/
|
*/
|
||||||
bytes? :boolean
|
bytes? :boolean
|
||||||
|
|
||||||
/** Allow "javascript:" in links */
|
|
||||||
allowJSURIs? :boolean
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional callback which if provided is called for each code block.
|
|
||||||
* langname holds the "language tag", if any, of the block.
|
|
||||||
*
|
|
||||||
* The returned value is inserted into the resulting HTML verbatim, without HTML escaping.
|
|
||||||
* Thus, you should take care of properly escaping any special HTML characters.
|
|
||||||
*
|
|
||||||
* If the function returns null or undefined, or an exception occurs, the body will be
|
|
||||||
* included as-is after going through HTML escaping.
|
|
||||||
*
|
|
||||||
* Note that use of this callback has an adverse impact on performance as it casues
|
|
||||||
* calls and data to be bridged between WASM and JS on every invocation.
|
|
||||||
*/
|
|
||||||
onCodeBlock? :(langname :string, body :UTF8Bytes) => Uint8Array|string|null|undefined
|
|
||||||
|
|
||||||
/** @depreceated use "bytes" instead (v1.1.1) */
|
/** @depreceated use "bytes" instead (v1.1.1) */
|
||||||
asMemoryView? :boolean
|
asMemoryView? :boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/** UTF8Bytes is a Uint8Array representing UTF8 text */
|
|
||||||
export interface UTF8Bytes extends Uint8Array {
|
|
||||||
/** toString returns a UTF8 decoded string (lazily decoded and cached) */
|
|
||||||
toString() :string
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Flags that customize Markdown parsing */
|
/** Flags that customize Markdown parsing */
|
||||||
export enum ParseFlags {
|
export enum ParseFlags {
|
||||||
/** In TEXT, collapse non-trivial whitespace into single ' ' */ COLLAPSE_WHITESPACE,
|
/** In TEXT, collapse non-trivial whitespace into single ' ' */ COLLAPSE_WHITESPACE,
|
||||||
|
|
|
||||||
28
package-lock.json
generated
28
package-lock.json
generated
|
|
@ -1,31 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "markdown-wasm",
|
"name": "markdown-wasm",
|
||||||
"version": "1.2.0",
|
"version": "1.1.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"version": "1.2.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"devDependencies": {
|
|
||||||
"wasmc": "^2.2.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wasmc": {
|
|
||||||
"version": "2.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/wasmc/-/wasmc-2.2.1.tgz",
|
|
||||||
"integrity": "sha512-uV/nhZdfnffBfLv2WJWWfRoJwk+5phhPobg7fqnJoRpXdUV5eShRb6tOXoco987cTxarkBymYwGufMdC4dPXAw==",
|
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
|
||||||
"wasmc": "wasmc"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wasmc": {
|
"wasmc": {
|
||||||
"version": "2.2.1",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/wasmc/-/wasmc-2.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/wasmc/-/wasmc-2.2.0.tgz",
|
||||||
"integrity": "sha512-uV/nhZdfnffBfLv2WJWWfRoJwk+5phhPobg7fqnJoRpXdUV5eShRb6tOXoco987cTxarkBymYwGufMdC4dPXAw==",
|
"integrity": "sha512-Pp2B42WuhL7JjslKDlGbpoV8WYpGAz0ct1XYTQWIN99cVsj7nfEuhD/jKwivLJHOFSOzMZZqvQH4m5BPQmTmCg==",
|
||||||
"dev": true
|
"dev": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "markdown-wasm",
|
"name": "markdown-wasm",
|
||||||
"version": "1.2.0",
|
"version": "1.1.2",
|
||||||
"description": "Markdown parser and html generator implemented in WebAssembly",
|
"description": "Markdown parser and html generator implemented in WebAssembly",
|
||||||
"browser": "dist/markdown.js",
|
"browser": "dist/markdown.js",
|
||||||
"main": "dist/markdown.node.js",
|
"main": "dist/markdown.node.js",
|
||||||
|
|
@ -35,6 +35,6 @@
|
||||||
"author": "Rasmus Andersson <https://rsms.me/>",
|
"author": "Rasmus Andersson <https://rsms.me/>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"wasmc": "^2.2.1"
|
"wasmc": "^2.2.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/common.h
16
src/common.h
|
|
@ -54,21 +54,7 @@ typedef int32_t i32;
|
||||||
#endif
|
#endif
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#define dlog(fmt, ...) printf(fmt "\n", ##__VA_ARGS__)
|
#define dlog(...) printf(__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
#define dlog(...)
|
#define dlog(...)
|
||||||
#endif /* DEBUG > 0 */
|
#endif /* DEBUG > 0 */
|
||||||
|
|
||||||
#include "wbuf.h"
|
|
||||||
|
|
||||||
// these should be in sync with "OutputFlags" in md.js
|
|
||||||
typedef enum OutputFlags {
|
|
||||||
OutputFlagHTML = 1 << 0,
|
|
||||||
OutputFlagXHTML = 1 << 1,
|
|
||||||
OutputFlagAllowJSURI = 1 << 2, // allow "javascript:" URIs in links
|
|
||||||
} OutputFlags;
|
|
||||||
|
|
||||||
typedef int(*JSTextFilterFun)(
|
|
||||||
const char* metaptr, u32 metalen,
|
|
||||||
const char* inptr, u32 inlen,
|
|
||||||
const char** outptrp); // return outlen
|
|
||||||
|
|
|
||||||
197
src/fmt_html.c
197
src/fmt_html.c
|
|
@ -25,18 +25,17 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <strings.h>
|
|
||||||
|
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "fmt_html.h"
|
#include "fmt_html.h"
|
||||||
#include "md4c.h"
|
#include "md4c.h"
|
||||||
|
|
||||||
// typedef struct FmtHTML_st {
|
typedef struct HtmlRenderer_st {
|
||||||
// WBuf* outbuf;
|
WBuf* outbuf;
|
||||||
// int imgnest;
|
int imgnest;
|
||||||
// int addanchor;
|
int addanchor;
|
||||||
// u32 flags;
|
u32 flags;
|
||||||
// } FmtHTML;
|
} HtmlRenderer;
|
||||||
|
|
||||||
|
|
||||||
static char htmlEscapeMap[256] = {
|
static char htmlEscapeMap[256] = {
|
||||||
|
|
@ -62,20 +61,20 @@ static char htmlEscapeMap[256] = {
|
||||||
static const char ucReplacementUTF8[] = { 0xef, 0xbf, 0xbd };
|
static const char ucReplacementUTF8[] = { 0xef, 0xbf, 0xbd };
|
||||||
|
|
||||||
|
|
||||||
static inline void render_text(FmtHTML* r, const char* pch, size_t len) {
|
static inline void render_text(HtmlRenderer* r, const char* pch, size_t len) {
|
||||||
WBufAppendBytes(r->outbuf, pch, len);
|
WBufAppendBytes(r->outbuf, pch, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void render_literal(FmtHTML* r, const char* cs) {
|
static inline void render_literal(HtmlRenderer* r, const char* cs) {
|
||||||
WBufAppendBytes(r->outbuf, cs, strlen(cs));
|
WBufAppendBytes(r->outbuf, cs, strlen(cs));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void render_char(FmtHTML* r, char c) {
|
static inline void render_char(HtmlRenderer* r, char c) {
|
||||||
WBufAppendc(r->outbuf, c);
|
WBufAppendc(r->outbuf, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void render_html_escaped(FmtHTML* r, const char* data, size_t size) {
|
static void render_html_escaped(HtmlRenderer* r, const char* data, size_t size) {
|
||||||
MD_OFFSET beg = 0;
|
MD_OFFSET beg = 0;
|
||||||
MD_OFFSET off = 0;
|
MD_OFFSET off = 0;
|
||||||
|
|
||||||
|
|
@ -168,7 +167,7 @@ static size_t WBufAppendSlug(WBuf* b, const char* pch, size_t len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void render_attribute(FmtHTML* r, const MD_ATTRIBUTE* attr) {
|
static void render_attribute(HtmlRenderer* r, const MD_ATTRIBUTE* attr) {
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; attr->substr_offsets[i] < attr->size; i++) {
|
for (i = 0; attr->substr_offsets[i] < attr->size; i++) {
|
||||||
MD_TEXTTYPE type = attr->substr_types[i];
|
MD_TEXTTYPE type = attr->substr_types[i];
|
||||||
|
|
@ -184,7 +183,7 @@ static void render_attribute(FmtHTML* r, const MD_ATTRIBUTE* attr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void render_open_ol_block(FmtHTML* r, const MD_BLOCK_OL_DETAIL* det) {
|
static void render_open_ol_block(HtmlRenderer* r, const MD_BLOCK_OL_DETAIL* det) {
|
||||||
if (det->start == 1) {
|
if (det->start == 1) {
|
||||||
render_literal(r, "<ol>\n");
|
render_literal(r, "<ol>\n");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -194,7 +193,7 @@ static void render_open_ol_block(FmtHTML* r, const MD_BLOCK_OL_DETAIL* det) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_open_li_block(FmtHTML* r, const MD_BLOCK_LI_DETAIL* det) {
|
static void render_open_li_block(HtmlRenderer* r, const MD_BLOCK_LI_DETAIL* det) {
|
||||||
if (det->is_task) {
|
if (det->is_task) {
|
||||||
render_literal(r, "<li class=\"task-list-item\"><input type=\"checkbox\" disabled");
|
render_literal(r, "<li class=\"task-list-item\"><input type=\"checkbox\" disabled");
|
||||||
if (det->task_mark == 'x' || det->task_mark == 'X') {
|
if (det->task_mark == 'x' || det->task_mark == 'X') {
|
||||||
|
|
@ -206,7 +205,7 @@ static void render_open_li_block(FmtHTML* r, const MD_BLOCK_LI_DETAIL* det) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_open_code_block(FmtHTML* r, const MD_BLOCK_CODE_DETAIL* det) {
|
static void render_open_code_block(HtmlRenderer* r, const MD_BLOCK_CODE_DETAIL* det) {
|
||||||
render_literal(r, "<pre><code");
|
render_literal(r, "<pre><code");
|
||||||
if (det->lang.text != NULL) {
|
if (det->lang.text != NULL) {
|
||||||
render_literal(r, " class=\"language-");
|
render_literal(r, " class=\"language-");
|
||||||
|
|
@ -214,41 +213,9 @@ static void render_open_code_block(FmtHTML* r, const MD_BLOCK_CODE_DETAIL* det)
|
||||||
render_char(r, '"');
|
render_char(r, '"');
|
||||||
}
|
}
|
||||||
render_char(r, '>');
|
render_char(r, '>');
|
||||||
r->codeBlockNest++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_close_code_block(FmtHTML* r, const MD_BLOCK_CODE_DETAIL* det) {
|
static void render_open_td_block(HtmlRenderer* r, bool isTH, const MD_BLOCK_TD_DETAIL* det) {
|
||||||
dlog("end code block (lang \"%.*s\")", (int)det->lang.size, det->lang.text);
|
|
||||||
|
|
||||||
r->codeBlockNest--;
|
|
||||||
|
|
||||||
if (r->onCodeBlock) {
|
|
||||||
const char* text = r->tmpbuf.start;
|
|
||||||
size_t len = WBufLen(&r->tmpbuf);
|
|
||||||
|
|
||||||
int outlen = -1;
|
|
||||||
|
|
||||||
if (len <= 0x7FFFFFFF) {
|
|
||||||
const char* outptr = NULL;
|
|
||||||
outlen = r->onCodeBlock(det->lang.text, (u32)det->lang.size, text, (u32)len, &outptr);
|
|
||||||
if (outlen > 0 && outptr != NULL)
|
|
||||||
WBufAppendBytes(r->outbuf, outptr, (size_t)outlen);
|
|
||||||
if (outptr != NULL)
|
|
||||||
free((void*)outptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (outlen < 0) {
|
|
||||||
// The function failed or opted out of taking care of formatting
|
|
||||||
render_html_escaped(r, text, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
WBufReset(&r->tmpbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
render_literal(r, "</code></pre>\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void render_open_td_block(FmtHTML* r, bool isTH, const MD_BLOCK_TD_DETAIL* det) {
|
|
||||||
render_text(r, isTH ? "<th" : "<td", 3);
|
render_text(r, isTH ? "<th" : "<td", 3);
|
||||||
switch (det->align) {
|
switch (det->align) {
|
||||||
case MD_ALIGN_LEFT: render_literal(r, " align=\"left\">"); break;
|
case MD_ALIGN_LEFT: render_literal(r, " align=\"left\">"); break;
|
||||||
|
|
@ -258,21 +225,9 @@ static void render_open_td_block(FmtHTML* r, bool isTH, const MD_BLOCK_TD_DETAIL
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool is_javascript_uri(const MD_CHAR* text, size_t len) {
|
static void render_open_a_span(HtmlRenderer* r, const MD_SPAN_A_DETAIL* det) {
|
||||||
return (
|
|
||||||
len >= strlen("javascript:") &&
|
|
||||||
strncasecmp(text, "javascript:", strlen("javascript:")) == 0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void render_open_a_span(FmtHTML* r, const MD_SPAN_A_DETAIL* det) {
|
|
||||||
render_literal(r, "<a href=\"");
|
render_literal(r, "<a href=\"");
|
||||||
// skip "javascript:" URIs unless explicitly allowed
|
render_attribute(r, &det->href);
|
||||||
if ((r->flags & OutputFlagAllowJSURI) != 0 ||
|
|
||||||
!is_javascript_uri(det->href.text, det->href.size))
|
|
||||||
{
|
|
||||||
render_attribute(r, &det->href);
|
|
||||||
}
|
|
||||||
if (det->title.text != NULL) {
|
if (det->title.text != NULL) {
|
||||||
render_literal(r, "\" title=\"");
|
render_literal(r, "\" title=\"");
|
||||||
render_attribute(r, &det->title);
|
render_attribute(r, &det->title);
|
||||||
|
|
@ -280,23 +235,23 @@ static void render_open_a_span(FmtHTML* r, const MD_SPAN_A_DETAIL* det) {
|
||||||
render_literal(r, "\">");
|
render_literal(r, "\">");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_open_img_span(FmtHTML* r, const MD_SPAN_IMG_DETAIL* det) {
|
static void render_open_img_span(HtmlRenderer* r, const MD_SPAN_IMG_DETAIL* det) {
|
||||||
render_literal(r, "<img src=\"");
|
render_literal(r, "<img src=\"");
|
||||||
render_attribute(r, &det->src);
|
render_attribute(r, &det->src);
|
||||||
render_literal(r, "\" alt=\"");
|
render_literal(r, "\" alt=\"");
|
||||||
r->imgnest++;
|
r->imgnest++;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_close_img_span(FmtHTML* r, const MD_SPAN_IMG_DETAIL* det) {
|
static void render_close_img_span(HtmlRenderer* r, const MD_SPAN_IMG_DETAIL* det) {
|
||||||
if(det->title.text != NULL) {
|
if(det->title.text != NULL) {
|
||||||
render_literal(r, "\" title=\"");
|
render_literal(r, "\" title=\"");
|
||||||
render_attribute(r, &det->title);
|
render_attribute(r, &det->title);
|
||||||
}
|
}
|
||||||
render_literal(r, (r->flags & OutputFlagXHTML) ? "\"/>" : "\">");
|
render_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "\"/>" : "\">");
|
||||||
r->imgnest--;
|
r->imgnest--;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_open_wikilink_span(FmtHTML* r, const MD_SPAN_WIKILINK_DETAIL* det) {
|
static void render_open_wikilink_span(HtmlRenderer* r, const MD_SPAN_WIKILINK_DETAIL* det) {
|
||||||
render_literal(r, "<x-wikilink data-target=\"");
|
render_literal(r, "<x-wikilink data-target=\"");
|
||||||
render_attribute(r, &det->target);
|
render_attribute(r, &det->target);
|
||||||
render_literal(r, "\">");
|
render_literal(r, "\">");
|
||||||
|
|
@ -311,30 +266,30 @@ static void render_open_wikilink_span(FmtHTML* r, const MD_SPAN_WIKILINK_DETAIL*
|
||||||
|
|
||||||
static int enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) {
|
static int enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) {
|
||||||
static const MD_CHAR* head[6] = { "<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<h6>" };
|
static const MD_CHAR* head[6] = { "<h1>", "<h2>", "<h3>", "<h4>", "<h5>", "<h6>" };
|
||||||
FmtHTML* r = (FmtHTML*) userdata;
|
HtmlRenderer* r = (HtmlRenderer*) userdata;
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case MD_BLOCK_DOC: /* noop */ break;
|
case MD_BLOCK_DOC: /* noop */ break;
|
||||||
case MD_BLOCK_QUOTE: render_literal(r, "<blockquote>\n"); break;
|
case MD_BLOCK_QUOTE: render_literal(r, "<blockquote>\n"); break;
|
||||||
case MD_BLOCK_UL: render_literal(r, "<ul>\n"); break;
|
case MD_BLOCK_UL: render_literal(r, "<ul>\n"); break;
|
||||||
case MD_BLOCK_OL: render_open_ol_block(r, (const MD_BLOCK_OL_DETAIL*)detail); break;
|
case MD_BLOCK_OL: render_open_ol_block(r, (const MD_BLOCK_OL_DETAIL*)detail); break;
|
||||||
case MD_BLOCK_LI: render_open_li_block(r, (const MD_BLOCK_LI_DETAIL*)detail); break;
|
case MD_BLOCK_LI: render_open_li_block(r, (const MD_BLOCK_LI_DETAIL*)detail); break;
|
||||||
case MD_BLOCK_HR: render_literal(r, (r->flags & OutputFlagXHTML) ? "<hr/>\n" : "<hr>\n"); break;
|
case MD_BLOCK_HR: render_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "<hr/>\n" : "<hr>\n"); break;
|
||||||
case MD_BLOCK_H:
|
case MD_BLOCK_H:
|
||||||
{
|
{
|
||||||
render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]);
|
render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]);
|
||||||
r->addanchor = 1;
|
r->addanchor = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case MD_BLOCK_CODE: render_open_code_block(r, (const MD_BLOCK_CODE_DETAIL*) detail); break;
|
case MD_BLOCK_CODE: render_open_code_block(r, (const MD_BLOCK_CODE_DETAIL*) detail); break;
|
||||||
case MD_BLOCK_HTML: /* noop */ break;
|
case MD_BLOCK_HTML: /* noop */ break;
|
||||||
case MD_BLOCK_P: render_literal(r, "<p>"); break;
|
case MD_BLOCK_P: render_literal(r, "<p>"); break;
|
||||||
case MD_BLOCK_TABLE: render_literal(r, "<table>\n"); break;
|
case MD_BLOCK_TABLE: render_literal(r, "<table>\n"); break;
|
||||||
case MD_BLOCK_THEAD: render_literal(r, "<thead>\n"); break;
|
case MD_BLOCK_THEAD: render_literal(r, "<thead>\n"); break;
|
||||||
case MD_BLOCK_TBODY: render_literal(r, "<tbody>\n"); break;
|
case MD_BLOCK_TBODY: render_literal(r, "<tbody>\n"); break;
|
||||||
case MD_BLOCK_TR: render_literal(r, "<tr>\n"); break;
|
case MD_BLOCK_TR: render_literal(r, "<tr>\n"); break;
|
||||||
case MD_BLOCK_TH: render_open_td_block(r, true, (MD_BLOCK_TD_DETAIL*)detail); break;
|
case MD_BLOCK_TH: render_open_td_block(r, true, (MD_BLOCK_TD_DETAIL*)detail); break;
|
||||||
case MD_BLOCK_TD: render_open_td_block(r, false, (MD_BLOCK_TD_DETAIL*)detail); break;
|
case MD_BLOCK_TD: render_open_td_block(r, false, (MD_BLOCK_TD_DETAIL*)detail); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -342,32 +297,32 @@ static int enter_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata)
|
||||||
|
|
||||||
static int leave_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) {
|
static int leave_block_callback(MD_BLOCKTYPE type, void* detail, void* userdata) {
|
||||||
static const MD_CHAR* head[6] = { "</h1>\n", "</h2>\n", "</h3>\n", "</h4>\n", "</h5>\n", "</h6>\n" };
|
static const MD_CHAR* head[6] = { "</h1>\n", "</h2>\n", "</h3>\n", "</h4>\n", "</h5>\n", "</h6>\n" };
|
||||||
FmtHTML* r = (FmtHTML*) userdata;
|
HtmlRenderer* r = (HtmlRenderer*) userdata;
|
||||||
|
|
||||||
switch(type) {
|
switch(type) {
|
||||||
case MD_BLOCK_DOC: /*noop*/ break;
|
case MD_BLOCK_DOC: /*noop*/ break;
|
||||||
case MD_BLOCK_QUOTE: render_literal(r, "</blockquote>\n"); break;
|
case MD_BLOCK_QUOTE: render_literal(r, "</blockquote>\n"); break;
|
||||||
case MD_BLOCK_UL: render_literal(r, "</ul>\n"); break;
|
case MD_BLOCK_UL: render_literal(r, "</ul>\n"); break;
|
||||||
case MD_BLOCK_OL: render_literal(r, "</ol>\n"); break;
|
case MD_BLOCK_OL: render_literal(r, "</ol>\n"); break;
|
||||||
case MD_BLOCK_LI: render_literal(r, "</li>\n"); break;
|
case MD_BLOCK_LI: render_literal(r, "</li>\n"); break;
|
||||||
case MD_BLOCK_HR: /*noop*/ break;
|
case MD_BLOCK_HR: /*noop*/ break;
|
||||||
case MD_BLOCK_H: render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break;
|
case MD_BLOCK_H: render_literal(r, head[((MD_BLOCK_H_DETAIL*)detail)->level - 1]); break;
|
||||||
case MD_BLOCK_CODE: render_close_code_block(r, (const MD_BLOCK_CODE_DETAIL*)detail); break;
|
case MD_BLOCK_CODE: render_literal(r, "</code></pre>\n"); break;
|
||||||
case MD_BLOCK_HTML: /* noop */ break;
|
case MD_BLOCK_HTML: /* noop */ break;
|
||||||
case MD_BLOCK_P: render_literal(r, "</p>\n"); break;
|
case MD_BLOCK_P: render_literal(r, "</p>\n"); break;
|
||||||
case MD_BLOCK_TABLE: render_literal(r, "</table>\n"); break;
|
case MD_BLOCK_TABLE: render_literal(r, "</table>\n"); break;
|
||||||
case MD_BLOCK_THEAD: render_literal(r, "</thead>\n"); break;
|
case MD_BLOCK_THEAD: render_literal(r, "</thead>\n"); break;
|
||||||
case MD_BLOCK_TBODY: render_literal(r, "</tbody>\n"); break;
|
case MD_BLOCK_TBODY: render_literal(r, "</tbody>\n"); break;
|
||||||
case MD_BLOCK_TR: render_literal(r, "</tr>\n"); break;
|
case MD_BLOCK_TR: render_literal(r, "</tr>\n"); break;
|
||||||
case MD_BLOCK_TH: render_literal(r, "</th>\n"); break;
|
case MD_BLOCK_TH: render_literal(r, "</th>\n"); break;
|
||||||
case MD_BLOCK_TD: render_literal(r, "</td>\n"); break;
|
case MD_BLOCK_TD: render_literal(r, "</td>\n"); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
|
static int enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
|
||||||
FmtHTML* r = (FmtHTML*) userdata;
|
HtmlRenderer* r = (HtmlRenderer*) userdata;
|
||||||
|
|
||||||
if(r->imgnest > 0) {
|
if(r->imgnest > 0) {
|
||||||
/* We are inside a Markdown image label. Markdown allows to use any
|
/* We are inside a Markdown image label. Markdown allows to use any
|
||||||
|
|
@ -392,8 +347,8 @@ static int enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
|
||||||
case MD_SPAN_EM: render_literal(r, "<em>"); break;
|
case MD_SPAN_EM: render_literal(r, "<em>"); break;
|
||||||
case MD_SPAN_STRONG: render_literal(r, "<b>"); break;
|
case MD_SPAN_STRONG: render_literal(r, "<b>"); break;
|
||||||
case MD_SPAN_U: render_literal(r, "<u>"); break;
|
case MD_SPAN_U: render_literal(r, "<u>"); break;
|
||||||
case MD_SPAN_A: render_open_a_span(r, (MD_SPAN_A_DETAIL*)detail); break;
|
case MD_SPAN_A: render_open_a_span(r, (MD_SPAN_A_DETAIL*) detail); break;
|
||||||
case MD_SPAN_IMG: render_open_img_span(r, (MD_SPAN_IMG_DETAIL*)detail); break;
|
case MD_SPAN_IMG: render_open_img_span(r, (MD_SPAN_IMG_DETAIL*) detail); break;
|
||||||
case MD_SPAN_CODE: render_literal(r, "<code>"); break;
|
case MD_SPAN_CODE: render_literal(r, "<code>"); break;
|
||||||
case MD_SPAN_DEL: render_literal(r, "<del>"); break;
|
case MD_SPAN_DEL: render_literal(r, "<del>"); break;
|
||||||
case MD_SPAN_LATEXMATH: render_literal(r, "<x-equation>"); break;
|
case MD_SPAN_LATEXMATH: render_literal(r, "<x-equation>"); break;
|
||||||
|
|
@ -405,7 +360,7 @@ static int enter_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
|
static int leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
|
||||||
FmtHTML* r = (FmtHTML*) userdata;
|
HtmlRenderer* r = (HtmlRenderer*) userdata;
|
||||||
|
|
||||||
if(r->imgnest > 0) {
|
if(r->imgnest > 0) {
|
||||||
/* Ditto as in enter_span_callback(), except we have to allow the
|
/* Ditto as in enter_span_callback(), except we have to allow the
|
||||||
|
|
@ -432,12 +387,7 @@ static int leave_span_callback(MD_SPANTYPE type, void* detail, void* userdata) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) {
|
static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, void* userdata) {
|
||||||
FmtHTML* r = (FmtHTML*) userdata;
|
HtmlRenderer* r = (HtmlRenderer*) userdata;
|
||||||
|
|
||||||
if (r->codeBlockNest && r->onCodeBlock) {
|
|
||||||
WBufAppendBytes(&r->tmpbuf, text, size);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r->addanchor) {
|
if (r->addanchor) {
|
||||||
r->addanchor = 0;
|
r->addanchor = 0;
|
||||||
|
|
@ -459,18 +409,18 @@ static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, vo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch(type) {
|
||||||
case MD_TEXT_NULLCHAR: render_text(r, ucReplacementUTF8, sizeof(ucReplacementUTF8)); break;
|
case MD_TEXT_NULLCHAR: render_text(r, ucReplacementUTF8, sizeof(ucReplacementUTF8)); break;
|
||||||
case MD_TEXT_BR:
|
case MD_TEXT_BR:
|
||||||
render_literal(
|
render_literal(
|
||||||
r,
|
r,
|
||||||
r->imgnest == 0 ?
|
r->imgnest == 0 ?
|
||||||
((r->flags & OutputFlagXHTML) ? "<br/>\n" : "<br>\n") :
|
((r->flags & MD_HTML_FLAG_XHTML) ? "<br/>\n" : "<br>\n") :
|
||||||
" "
|
" "
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
render_literal(r, (r->flags & OutputFlagXHTML) ? "<hr/>\n" : "<hr>\n"); break;
|
render_literal(r, (r->flags & MD_HTML_FLAG_XHTML) ? "<hr/>\n" : "<hr>\n"); break;
|
||||||
|
|
||||||
case MD_TEXT_SOFTBR: render_literal(r, (r->imgnest == 0 ? "\n" : " ")); break;
|
case MD_TEXT_SOFTBR: render_literal(r, (r->imgnest == 0 ? "\n" : " ")); break;
|
||||||
case MD_TEXT_HTML: render_text(r, text, size); break;
|
case MD_TEXT_HTML: render_text(r, text, size); break;
|
||||||
|
|
@ -485,15 +435,18 @@ static int text_callback(MD_TEXTTYPE type, const MD_CHAR* text, MD_SIZE size, vo
|
||||||
// dlog("MD4C: %s\n", msg);
|
// dlog("MD4C: %s\n", msg);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
int fmt_html(const MD_CHAR* input, MD_SIZE input_size, FmtHTML* fmt) {
|
int fmt_html(
|
||||||
fmt->imgnest = 0;
|
const MD_CHAR* input,
|
||||||
fmt->addanchor = 0;
|
MD_SIZE input_size,
|
||||||
fmt->codeBlockNest = 0;
|
WBuf* outbuf,
|
||||||
fmt->tmpbuf = (WBuf){0};
|
u32 parser_flags,
|
||||||
|
u32 render_flags
|
||||||
|
) {
|
||||||
|
HtmlRenderer render = { outbuf, 0, 0, render_flags };
|
||||||
|
|
||||||
MD_PARSER parser = {
|
MD_PARSER parser = {
|
||||||
0,
|
0,
|
||||||
fmt->parserFlags,
|
parser_flags,
|
||||||
enter_block_callback,
|
enter_block_callback,
|
||||||
leave_block_callback,
|
leave_block_callback,
|
||||||
enter_span_callback,
|
enter_span_callback,
|
||||||
|
|
@ -503,11 +456,5 @@ int fmt_html(const MD_CHAR* input, MD_SIZE input_size, FmtHTML* fmt) {
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
WBufInit(&fmt->tmpbuf);
|
return md_parse(input, input_size, &parser, (void*) &render);
|
||||||
|
|
||||||
int res = md_parse(input, input_size, &parser, (void*)fmt);
|
|
||||||
|
|
||||||
WBufFree(&fmt->tmpbuf);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "wbuf.h"
|
||||||
|
|
||||||
typedef struct FmtHTML {
|
#define MD_HTML_FLAG_XHTML 0x0008 // instead of e.g. <br>, generate <br/>
|
||||||
OutputFlags flags;
|
|
||||||
u32 parserFlags; // passed along to md_parse
|
|
||||||
WBuf* outbuf;
|
|
||||||
|
|
||||||
// optional callbacks
|
int fmt_html(const char* input, u32 inputlen, WBuf* outbuf, u32 parserFlags, u32 renderFlags);
|
||||||
JSTextFilterFun onCodeBlock;
|
|
||||||
|
|
||||||
// internal state
|
|
||||||
int imgnest;
|
|
||||||
int addanchor;
|
|
||||||
int codeBlockNest;
|
|
||||||
WBuf tmpbuf;
|
|
||||||
} FmtHTML;
|
|
||||||
|
|
||||||
int fmt_html(const char* input, u32 inputlen, FmtHTML* fmt);
|
|
||||||
|
|
|
||||||
28
src/md.c
28
src/md.c
|
|
@ -1,9 +1,16 @@
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
#include "wlib.h"
|
#include "wlib.h"
|
||||||
|
#include "wbuf.h"
|
||||||
#include "fmt_html.h"
|
#include "fmt_html.h"
|
||||||
// #include "fmt_json.h"
|
// #include "fmt_json.h"
|
||||||
|
|
||||||
|
// these should be in sync with "OutputFlags" in md.js
|
||||||
|
typedef enum OutputFlags {
|
||||||
|
OutputFlagHTML = 1 << 0,
|
||||||
|
OutputFlagXHTML = 1 << 1,
|
||||||
|
} OutputFlags;
|
||||||
|
|
||||||
typedef enum ErrorCode {
|
typedef enum ErrorCode {
|
||||||
ERR_NONE,
|
ERR_NONE,
|
||||||
ERR_MD_PARSE,
|
ERR_MD_PARSE,
|
||||||
|
|
@ -13,7 +20,7 @@ typedef enum ErrorCode {
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
void __attribute__((constructor)) init() {
|
void __attribute__((constructor)) init() {
|
||||||
dlog("WASM INIT");
|
dlog("WASM INIT\n");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -28,24 +35,21 @@ export size_t parseUTF8(
|
||||||
u32 inbuflen,
|
u32 inbuflen,
|
||||||
u32 parser_flags,
|
u32 parser_flags,
|
||||||
OutputFlags outflags,
|
OutputFlags outflags,
|
||||||
const char** outptr,
|
const char** outptr
|
||||||
JSTextFilterFun onCodeBlock
|
|
||||||
) {
|
) {
|
||||||
dlog("parseUTF8 called with inbufptr=%p inbuflen=%u", inbufptr, inbuflen);
|
dlog("parseUTF8 called with inbufptr=%p inbuflen=%u\n", inbufptr, inbuflen);
|
||||||
|
|
||||||
WBufReset(&outbuf);
|
WBufReset(&outbuf);
|
||||||
|
|
||||||
if ((outflags & OutputFlagHTML) || (outflags & OutputFlagXHTML)) {
|
if (outflags & OutputFlagHTML) {
|
||||||
WBufReserve(&outbuf, inbuflen * 2); // approximate output size to minimize reallocations
|
WBufReserve(&outbuf, inbuflen * 2); // approximate output size to minimize reallocations
|
||||||
|
|
||||||
FmtHTML fmt = {
|
u32 render_flags = 0;
|
||||||
.flags = outflags,
|
if (outflags & OutputFlagXHTML) {
|
||||||
.parserFlags = parser_flags,
|
render_flags |= MD_HTML_FLAG_XHTML;
|
||||||
.outbuf = &outbuf,
|
}
|
||||||
.onCodeBlock = onCodeBlock,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (fmt_html(inbufptr, inbuflen, &fmt) != 0) {
|
if (fmt_html(inbufptr, inbuflen, &outbuf, parser_flags, render_flags) != 0) {
|
||||||
// fmt_html returns status of md_parse which only fails in extreme cases
|
// fmt_html returns status of md_parse which only fails in extreme cases
|
||||||
// like when out of memory. md4c does not provide error codes or error messages.
|
// like when out of memory. md4c does not provide error codes or error messages.
|
||||||
WErrSet(ERR_MD_PARSE, "md parser error");
|
WErrSet(ERR_MD_PARSE, "md parser error");
|
||||||
|
|
|
||||||
86
src/md.js
86
src/md.js
|
|
@ -1,10 +1,4 @@
|
||||||
import {
|
import { utf8, withTmpBytePtr, withOutPtr, werrCheck } from "./wlib"
|
||||||
utf8,
|
|
||||||
withTmpBytePtr,
|
|
||||||
withOutPtr,
|
|
||||||
werrCheck,
|
|
||||||
mallocbuf,
|
|
||||||
} from "./wlib"
|
|
||||||
|
|
||||||
export const ready = Module.ready
|
export const ready = Module.ready
|
||||||
|
|
||||||
|
|
@ -41,14 +35,12 @@ export const ParseFlags = {
|
||||||
NO_HTML: 0x0020 | 0x0040, // NO_HTML_BLOCKS | NO_HTML_SPANS
|
NO_HTML: 0x0020 | 0x0040, // NO_HTML_BLOCKS | NO_HTML_SPANS
|
||||||
}
|
}
|
||||||
|
|
||||||
// these should be in sync with "OutputFlags" in common.h
|
// these should be in sync with "OutputFlags" in md.c
|
||||||
const OutputFlags = {
|
const OutputFlags = {
|
||||||
HTML: 1 << 0, // Output HTML
|
HTML: 1 << 0, // Output HTML
|
||||||
XHTML: 1 << 1, // Output XHTML (only has effect with HTML flag set)
|
XHTML: 1 << 1, // Output XHTML (only has effect with HTML flag set)
|
||||||
AllowJSURI: 1 << 2, // Allow "javascript:" URIs
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function parse(source, options) {
|
export function parse(source, options) {
|
||||||
options = options || {}
|
options = options || {}
|
||||||
|
|
||||||
|
|
@ -57,8 +49,7 @@ export function parse(source, options) {
|
||||||
options.parseFlags
|
options.parseFlags
|
||||||
)
|
)
|
||||||
|
|
||||||
let outputFlags = options.allowJSURIs ? OutputFlags.AllowJSURI : 0
|
let outputFlags = 0
|
||||||
|
|
||||||
switch (options.format) {
|
switch (options.format) {
|
||||||
case "xhtml":
|
case "xhtml":
|
||||||
outputFlags |= OutputFlags.HTML | OutputFlags.XHTML
|
outputFlags |= OutputFlags.HTML | OutputFlags.XHTML
|
||||||
|
|
@ -75,16 +66,11 @@ export function parse(source, options) {
|
||||||
throw new Error(`invalid format "${options.format}"`)
|
throw new Error(`invalid format "${options.format}"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
let onCodeBlockPtr = options.onCodeBlock ? create_onCodeBlock_fn(options.onCodeBlock) : 0
|
let buf = typeof source == "string" ? utf8.encode(source) : source
|
||||||
|
|
||||||
let buf = as_byte_array(source)
|
|
||||||
let outbuf = withOutPtr(outptr => withTmpBytePtr(buf, (inptr, inlen) =>
|
let outbuf = withOutPtr(outptr => withTmpBytePtr(buf, (inptr, inlen) =>
|
||||||
_parseUTF8(inptr, inlen, parseFlags, outputFlags, outptr, onCodeBlockPtr)
|
_parseUTF8(inptr, inlen, parseFlags, outputFlags, outptr)
|
||||||
))
|
))
|
||||||
|
|
||||||
if (options.onCodeBlock)
|
|
||||||
removeFunction(onCodeBlockPtr)
|
|
||||||
|
|
||||||
// check for error and throw if needed
|
// check for error and throw if needed
|
||||||
werrCheck()
|
werrCheck()
|
||||||
|
|
||||||
|
|
@ -93,62 +79,8 @@ export function parse(source, options) {
|
||||||
// console.log(utf8.decode(outbuf))
|
// console.log(utf8.decode(outbuf))
|
||||||
// }
|
// }
|
||||||
|
|
||||||
if (options.bytes || options.asMemoryView)
|
if (options.bytes || options.asMemoryView) {
|
||||||
return outbuf
|
return outbuf
|
||||||
|
}
|
||||||
return utf8.decode(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)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
#include "common.h"
|
#include "wbuf.h"
|
||||||
|
|
||||||
void WBufInit(WBuf* b) {
|
void WBufInit(WBuf* b) {
|
||||||
b->start = 0;
|
b->start = 0;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
typedef struct WBuf_s {
|
typedef struct WBuf_s {
|
||||||
char* start; // pointer to start of data
|
char* start; // pointer to start of data
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ const Path = require('path')
|
||||||
const commonmark = require('commonmark')
|
const commonmark = require('commonmark')
|
||||||
const Showdown = require('showdown')
|
const Showdown = require('showdown')
|
||||||
const marked = require('marked')
|
const marked = require('marked')
|
||||||
const { Remarkable } = require('remarkable');
|
|
||||||
const markdownit = require('markdown-it')('commonmark')
|
const markdownit = require('markdown-it')('commonmark')
|
||||||
const markdown_wasm = require('../../dist/markdown.node.js')
|
const markdown_wasm = require('../../dist/markdown.node.js')
|
||||||
|
|
||||||
|
|
@ -23,9 +22,6 @@ var showdown = new Showdown.Converter()
|
||||||
var parser = new commonmark.Parser()
|
var parser = new commonmark.Parser()
|
||||||
var renderer = new commonmark.HtmlRenderer()
|
var renderer = new commonmark.HtmlRenderer()
|
||||||
|
|
||||||
// setup remarkable
|
|
||||||
var remarkable = new Remarkable();
|
|
||||||
|
|
||||||
// parse CLI input
|
// parse CLI input
|
||||||
let filename = process.argv[2]
|
let filename = process.argv[2]
|
||||||
if (!filename) {
|
if (!filename) {
|
||||||
|
|
@ -80,9 +76,6 @@ function benchmarkFile(benchfile) {
|
||||||
.add('marked', function() {
|
.add('marked', function() {
|
||||||
marked(contents);
|
marked(contents);
|
||||||
})
|
})
|
||||||
.add('remarkable', function() {
|
|
||||||
remarkable.render(contents);
|
|
||||||
})
|
|
||||||
.add('markdown-it', function() {
|
.add('markdown-it', function() {
|
||||||
markdownit.render(contents);
|
markdownit.render(contents);
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,6 @@
|
||||||
"d3-node": "^2.2.2",
|
"d3-node": "^2.2.2",
|
||||||
"markdown-it": "^10.0.0",
|
"markdown-it": "^10.0.0",
|
||||||
"marked": "^0.7.0",
|
"marked": "^0.7.0",
|
||||||
"remarkable": "^2.0.1",
|
|
||||||
"showdown": "^1.9.1",
|
"showdown": "^1.9.1",
|
||||||
"svgo": "^1.3.2"
|
"svgo": "^1.3.2"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="840" height="300" font-family="-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif" font-size="16"><g text-anchor="end" font-weight="500"><text alignment-baseline="central" x="156" y="68" dx="-10">markdown-wasm</text><text alignment-baseline="central" x="156" y="104" dx="-10">remarkable</text><text alignment-baseline="central" x="156" y="140" dx="-10">markdown-it</text><text alignment-baseline="central" x="156" y="176" dx="-10">marked</text><text alignment-baseline="central" x="156" y="212" dx="-10">commonmark</text><text alignment-baseline="central" x="156" y="248" dx="-10">showdown</text></g><path fill="#4e79a7" d="M156 52h668v32H156z"/><path fill="#f28e2c" d="M156 88h453.666v32H156z"/><path fill="#e15759" d="M156 124h300.527v32H156z"/><path fill="#76b7b2" d="M156 160h259.363v32H156z"/><path fill="#59a14f" d="M156 196h210.906v32H156z"/><path fill="#edc949" d="M156 232h30.517v32H156z"/><g fill="#fff" text-anchor="end" font-weight="500"><text x="824" y="68" alignment-baseline="central" dx="-5">136,888</text><text x="609.666" y="104" alignment-baseline="central" dx="-5">92,966</text><text x="456.527" y="140" alignment-baseline="central" dx="-5">61,584</text><text x="415.363" y="176" alignment-baseline="central" dx="-5">53,149</text><text x="366.906" y="212" alignment-baseline="central" dx="-5">43,219</text><text x="186.517" y="248" alignment-baseline="central" dx="5" fill="#000" text-anchor="start">6,254</text></g><g fill="none" font-size="12.8" text-anchor="middle" opacity=".5"><g class="tick"><path stroke="currentColor" d="M156.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(156.5 48)">0</text></g><g class="tick"><path stroke="currentColor" d="M254.098 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(254.098 48)">20,000</text></g><g class="tick"><path stroke="currentColor" d="M351.697 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(351.697 48)">40,000</text></g><g class="tick"><path stroke="currentColor" d="M449.295 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(449.295 48)">60,000</text></g><g class="tick"><path stroke="currentColor" d="M546.893 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(546.893 48)">80,000</text></g><g class="tick"><path stroke="currentColor" d="M644.491 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(644.491 48)">100,000</text></g><g class="tick"><path stroke="currentColor" d="M742.09 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(742.09 48)">120,000</text></g></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="840" height="264" font-family="-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif" font-size="16"><g text-anchor="end" font-weight="500"><text alignment-baseline="central" x="156" y="68" dx="-10">markdown-wasm</text><text alignment-baseline="central" x="156" y="104" dx="-10">markdown-it</text><text alignment-baseline="central" x="156" y="140" dx="-10">marked</text><text alignment-baseline="central" x="156" y="176" dx="-10">commonmark</text><text alignment-baseline="central" x="156" y="212" dx="-10">showdown</text></g><path fill="#4e79a7" d="M156 52h668v32H156z"/><path fill="#f28e2c" d="M156 88h290.307v32H156z"/><path fill="#e15759" d="M156 124h263.486v32H156z"/><path fill="#76b7b2" d="M156 160h226.65v32H156z"/><path fill="#59a14f" d="M156 196h29.116v32H156z"/><g fill="#fff" text-anchor="end" font-weight="500"><text x="824" y="68" alignment-baseline="central" dx="-5">195,065</text><text x="446.307" y="104" alignment-baseline="central" dx="-5">84,774</text><text x="419.486" y="140" alignment-baseline="central" dx="-5">76,942</text><text x="382.65" y="176" alignment-baseline="central" dx="-5">66,185</text><text x="185.116" y="212" alignment-baseline="central" dx="5" fill="#000" text-anchor="start">8,502</text></g><g fill="none" font-size="12.8" text-anchor="middle" opacity=".5"><g class="tick"><path stroke="currentColor" d="M156.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(156.5 48)">0</text></g><g class="tick"><path stroke="currentColor" d="M224.99 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(224.99 48)">20,000</text></g><g class="tick"><path stroke="currentColor" d="M293.48 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(293.48 48)">40,000</text></g><g class="tick"><path stroke="currentColor" d="M361.97 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(361.97 48)">60,000</text></g><g class="tick"><path stroke="currentColor" d="M430.46 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(430.46 48)">80,000</text></g><g class="tick"><path stroke="currentColor" d="M498.95 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(498.95 48)">100,000</text></g><g class="tick"><path stroke="currentColor" d="M567.44 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(567.44 48)">120,000</text></g><g class="tick"><path stroke="currentColor" d="M635.93 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(635.93 48)">140,000</text></g><g class="tick"><path stroke="currentColor" d="M704.42 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(704.42 48)">160,000</text></g><g class="tick"><path stroke="currentColor" d="M772.909 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(772.909 48)">180,000</text></g></g></svg>
|
||||||
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.9 KiB |
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 5.1 KiB |
|
|
@ -1 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="840" height="300" font-family="-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif" font-size="16"><path fill="#4e79a7" d="M45 52h483v32H45z"/><path fill="#f28e2c" d="M16 88h582v32H16z"/><path fill="#76b7b2" d="M38 124h584v32H38z"/><path fill="#e15759" d="M47 160h578v32H47z"/><path fill="#59a14f" d="M96 196h553v32H96z"/><path fill="#edc949" d="M258 232h566v32H258z"/><g fill="#fff" font-weight="500" text-anchor="middle"><text x="286.5" y="68" alignment-baseline="central">markdown-wasm</text><text x="307" y="104" alignment-baseline="central">remarkable</text><text x="330" y="140" alignment-baseline="central">marked</text><text x="336" y="176" alignment-baseline="central">markdown-it</text><text x="372.5" y="212" alignment-baseline="central">commonmark</text><text x="541" y="248" alignment-baseline="central">showdown</text></g><g fill="#fff" font-weight="500"><text x="45" y="68" alignment-baseline="central" dx="5">2.7us</text><text x="16" y="104" alignment-baseline="central" dx="5">1.8us</text><text x="38" y="140" alignment-baseline="central" dx="5">2.5us</text><text x="47" y="176" alignment-baseline="central" dx="5">2.8us</text><text x="96" y="212" alignment-baseline="central" dx="5">5.7us</text><text x="258" y="248" alignment-baseline="central" dx="5">59.6us</text></g><g fill="#fff" font-weight="500" text-anchor="end"><text x="528" y="68" alignment-baseline="central" dx="-5">3.1ms</text><text x="598" y="104" alignment-baseline="central" dx="-5">8.4ms</text><text x="622" y="140" alignment-baseline="central" dx="-5">12.0ms</text><text x="625" y="176" alignment-baseline="central" dx="-5">12.5ms</text><text x="649" y="212" alignment-baseline="central" dx="-5">17.9ms</text><text x="824" y="248" alignment-baseline="central" dx="-5">226.4ms</text></g><g fill="none" font-size="12.8" text-anchor="middle" opacity=".5"><path stroke="currentColor" d="M24.5 48v-6M52.5 48v-6M72.5 48v-6M87.5 48v-6M100.5 48v-6M111.5 48v-6M120.5 48v-6M128.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M135.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(135.5 48)">10.0us</text></g><path stroke="currentColor" d="M183.5 48v-6M211.5 48v-6M230.5 48v-6M246.5 48v-6M258.5 48v-6M269.5 48v-6M278.5 48v-6M286.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M293.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(293.5 48)">100.0us</text></g><path stroke="currentColor" d="M341.5 48v-6M369.5 48v-6M388.5 48v-6M404.5 48v-6M416.5 48v-6M427.5 48v-6M436.5 48v-6M444.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M451.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(451.5 48)">1000.0us</text></g><path stroke="currentColor" d="M499.5 48v-6M527.5 48v-6M547.5 48v-6M562.5 48v-6M575.5 48v-6M585.5 48v-6M594.5 48v-6M602.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M610.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(610.5 48)">10.0ms</text></g><path stroke="currentColor" d="M657.5 48v-6M685.5 48v-6M705.5 48v-6M720.5 48v-6M733.5 48v-6M743.5 48v-6M753.5 48v-6M761.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M768.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(768.5 48)">100.0ms</text></g><path stroke="currentColor" d="M815.5 48v-6"/></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="840" height="264" font-family="-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif" font-size="16"><path fill="#4e79a7" d="M21 52h486v32H21z"/><path fill="#e15759" d="M16 88h576v32H16z"/><path fill="#f28e2c" d="M21 124h573v32H21z"/><path fill="#76b7b2" d="M70 160h541v32H70z"/><path fill="#59a14f" d="M227 196h597v32H227z"/><g fill="#fff" font-weight="500" text-anchor="middle"><text x="264" y="68" alignment-baseline="central">markdown-wasm</text><text x="304" y="104" alignment-baseline="central">marked</text><text x="307.5" y="140" alignment-baseline="central">markdown-it</text><text x="340.5" y="176" alignment-baseline="central">commonmark</text><text x="525.5" y="212" alignment-baseline="central">showdown</text></g><g fill="#fff" font-weight="500"><text x="21" y="68" alignment-baseline="central" dx="5">2.0us</text><text x="16" y="104" alignment-baseline="central" dx="5">1.8us</text><text x="21" y="140" alignment-baseline="central" dx="5">2.0us</text><text x="70" y="176" alignment-baseline="central" dx="5">4.0us</text><text x="227" y="212" alignment-baseline="central" dx="5">40.8us</text></g><g fill="#fff" font-weight="500" text-anchor="end"><text x="507" y="68" alignment-baseline="central" dx="-5">2.5ms</text><text x="592" y="104" alignment-baseline="central" dx="-5">8.6ms</text><text x="594" y="140" alignment-baseline="central" dx="-5">8.9ms</text><text x="611" y="176" alignment-baseline="central" dx="-5">11.4ms</text><text x="824" y="212" alignment-baseline="central" dx="-5">262.8ms</text></g><g fill="none" font-size="12.8" text-anchor="middle" opacity=".5"><path stroke="currentColor" d="M22.5 48v-6M50.5 48v-6M69.5 48v-6M85.5 48v-6M97.5 48v-6M108.5 48v-6M117.5 48v-6M125.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M132.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(132.5 48)">10.0us</text></g><path stroke="currentColor" d="M179.5 48v-6M207.5 48v-6M226.5 48v-6M241.5 48v-6M254.5 48v-6M264.5 48v-6M273.5 48v-6M281.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M288.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(288.5 48)">100.0us</text></g><path stroke="currentColor" d="M336.5 48v-6M363.5 48v-6M383.5 48v-6M398.5 48v-6M410.5 48v-6M421.5 48v-6M430.5 48v-6M438.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M445.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(445.5 48)">1000.0us</text></g><path stroke="currentColor" d="M492.5 48v-6M520.5 48v-6M539.5 48v-6M555.5 48v-6M567.5 48v-6M577.5 48v-6M586.5 48v-6M594.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M602.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(602.5 48)">10.0ms</text></g><path stroke="currentColor" d="M649.5 48v-6M676.5 48v-6M696.5 48v-6M711.5 48v-6M724.5 48v-6M734.5 48v-6M743.5 48v-6M751.5 48v-6"/><g class="tick"><path stroke="currentColor" d="M758.5 48v-6"/><text fill="currentColor" y="-9" dy="0em" transform="translate(758.5 48)">100.0ms</text></g><path stroke="currentColor" d="M805.5 48v-6"/></g></svg>
|
||||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.1 KiB |
4
wasmc.js
4
wasmc.js
|
|
@ -23,10 +23,6 @@ const m = {
|
||||||
] : [
|
] : [
|
||||||
// release flags
|
// release flags
|
||||||
]),
|
]),
|
||||||
lflags: [
|
|
||||||
// force inclusion of addFunction & removeFunction in release builds (emcc bug?)
|
|
||||||
"-s","EXPORTED_RUNTIME_METHODS=addFunction,removeFunction",
|
|
||||||
],
|
|
||||||
constants: {
|
constants: {
|
||||||
VERSION: package.version,
|
VERSION: package.version,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue