|
|
|
@ -11,6 +11,26 @@ export const tokenize = (gemtext) => |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
export function toHTML(gemtext, options) { |
|
|
|
|
options.inlineCSS = options.inlineCSS || CSS.inline(options); |
|
|
|
|
options.styleTag = options.styleTag || CSS.style(options); |
|
|
|
|
|
|
|
|
|
const tokens = tokenize(gemtext); |
|
|
|
|
|
|
|
|
|
if (options.body) return body(tokens, options); |
|
|
|
|
|
|
|
|
|
return `<!DOCTYPE html>
|
|
|
|
|
<html lang="${options.html}" dir="${ |
|
|
|
|
options.dir || "ltr" |
|
|
|
|
}" style='${options.inlineCSS("html")}'> |
|
|
|
|
<head>${head(tokens, options)}</head> |
|
|
|
|
<body${options.inlineCSS("body")}> |
|
|
|
|
${body(tokens, options)} |
|
|
|
|
</body> |
|
|
|
|
</html> |
|
|
|
|
`;
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function block( |
|
|
|
|
{ text, href, title, pre, alt, h1, h2, h3, li, quote }, |
|
|
|
|
options = {} |
|
|
|
@ -21,6 +41,14 @@ export function block( |
|
|
|
|
if (text) { |
|
|
|
|
content = text; |
|
|
|
|
} |
|
|
|
|
if (li) { |
|
|
|
|
type = "li"; |
|
|
|
|
content = li; |
|
|
|
|
} |
|
|
|
|
if (quote) { |
|
|
|
|
type = "blockquote"; |
|
|
|
|
content = quote; |
|
|
|
|
} |
|
|
|
|
if (h1) { |
|
|
|
|
type = "h1"; |
|
|
|
|
content = h1; |
|
|
|
@ -33,17 +61,10 @@ export function block( |
|
|
|
|
type = "h3"; |
|
|
|
|
content = h3; |
|
|
|
|
} |
|
|
|
|
if (li) { |
|
|
|
|
type = "li"; |
|
|
|
|
content = li; |
|
|
|
|
} |
|
|
|
|
if (quote) { |
|
|
|
|
type = "blockquote"; |
|
|
|
|
content = quote; |
|
|
|
|
} |
|
|
|
|
if (href) { |
|
|
|
|
const matchesExt = (url, exts) => |
|
|
|
|
exts.some((ext) => new RegExp(`\.${ext}$`).test(url)); |
|
|
|
|
|
|
|
|
|
if (options.image && matchesExt(href, options.image)) { |
|
|
|
|
type = "img"; |
|
|
|
|
props += ` src="${href}"`; |
|
|
|
@ -62,10 +83,12 @@ export function block( |
|
|
|
|
props += ` href="${href}"`; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (options.body || options.inline) |
|
|
|
|
|
|
|
|
|
if (options.body || options.inline) { |
|
|
|
|
props += options.inlineCSS( |
|
|
|
|
type === "p" && content === "" ? "p:empty" : type |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return `<${type}${props}>${escape(content)}</${type}>`; |
|
|
|
|
} |
|
|
|
@ -102,65 +125,36 @@ export function body(tokens, options) { |
|
|
|
|
return blocks.join("\n"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function toHTML(gemtext, options) { |
|
|
|
|
options.inlineCSS = options.inlineCSS || CSS.inline(options); |
|
|
|
|
options.styleTag = options.styleTag || CSS.style(options); |
|
|
|
|
|
|
|
|
|
const tokens = tokenize(gemtext); |
|
|
|
|
|
|
|
|
|
if (options.body) return body(tokens, options); |
|
|
|
|
export function head(tokens, options) { |
|
|
|
|
const truncate = (text, limit) => |
|
|
|
|
text.length > limit ? `${text.substring(0, limit)}...` : text; |
|
|
|
|
|
|
|
|
|
return `<!DOCTYPE html>
|
|
|
|
|
<html lang="${options.html}" dir="${ |
|
|
|
|
options.dir || "ltr" |
|
|
|
|
}" style='${options.inlineCSS("html")}'> |
|
|
|
|
<head>${head( |
|
|
|
|
Object.assign(options, { |
|
|
|
|
title: tokens[0].h1, |
|
|
|
|
description: description(tokens, options), |
|
|
|
|
}) |
|
|
|
|
)}</head> |
|
|
|
|
<body${options.inlineCSS("body")}> |
|
|
|
|
${body(tokens, options)} |
|
|
|
|
</body> |
|
|
|
|
</html> |
|
|
|
|
`;
|
|
|
|
|
} |
|
|
|
|
let description = |
|
|
|
|
options.description > 0 |
|
|
|
|
? tokens.find((token) => { |
|
|
|
|
return token.text && token.text !== ""; |
|
|
|
|
}) |
|
|
|
|
: false; |
|
|
|
|
|
|
|
|
|
export function head(options) { |
|
|
|
|
return ` |
|
|
|
|
<meta charset="${options.charset || "utf-8"}"> |
|
|
|
|
<meta name="viewport" content="width=device-width,initial-scale=1">${ |
|
|
|
|
options.styleTag |
|
|
|
|
} |
|
|
|
|
<title>${options.title}</title>${ |
|
|
|
|
!options.author ? "" : `\n<meta name="author" content="${options.author}">` |
|
|
|
|
<title>${tokens.find(({h1}) => h1).h1 || ""}</title>${ |
|
|
|
|
options.author ? `\n<meta name="author" content="${options.author}">` : ""
|
|
|
|
|
}${ |
|
|
|
|
!options.description |
|
|
|
|
? "" |
|
|
|
|
: `\n<meta name="description" content="${escape(options.description)}">` |
|
|
|
|
description |
|
|
|
|
? `\n<meta name="description" content="${escape(truncate(description.text, options.description))}">` |
|
|
|
|
: "" |
|
|
|
|
}${ |
|
|
|
|
!options.canonical |
|
|
|
|
? "" |
|
|
|
|
: `\n<link rel="canonical" href="${options.canonical}">` |
|
|
|
|
options.canonical |
|
|
|
|
? `\n<link rel="canonical" href="${options.canonical}">` |
|
|
|
|
: "" |
|
|
|
|
} |
|
|
|
|
`;
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function description(tokens, options) { |
|
|
|
|
const truncate = (text, limit) => |
|
|
|
|
text.length > limit ? `${text.substring(0, limit)}...` : text; |
|
|
|
|
|
|
|
|
|
let description = |
|
|
|
|
options.description > 0 |
|
|
|
|
? tokens.find((token) => { |
|
|
|
|
return token.text && token.text !== ""; |
|
|
|
|
}) |
|
|
|
|
: false; |
|
|
|
|
|
|
|
|
|
return description && truncate(description.text, options.description); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export const GMI_EXT = /\.gmi$/; |
|
|
|
|
|
|
|
|
|
export function streamHTML(options) { |
|
|
|
|