refactor html.js

This commit is contained in:
Talon Poole 2021-02-25 17:02:35 +00:00
parent cae16d74db
commit 62d529a633
3 changed files with 60 additions and 65 deletions

106
html.js
View file

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

View file

@ -178,7 +178,7 @@ test("--image, --audio, --video", () => {
}); });
test("tokenize", () => { test("tokenize", () => {
expect(tokenize(gemtext, { body: true })).toMatchInlineSnapshot(` expect(tokenize(gemtext)).toMatchInlineSnapshot(`
Array [ Array [
Object { Object {
"h1": "gmi-web", "h1": "gmi-web",

View file

@ -1,7 +1,7 @@
{ {
"name": "gmi-web-cli", "name": "gmi-web-cli",
"version": "1.0.0-rc.4", "version": "1.0.0-rc.4",
"description": "A bridge between HTML and Gemini", "description": "A bridge between Gemini and HTML",
"main": "html.js", "main": "html.js",
"type": "module", "type": "module",
"bin": { "bin": {
@ -11,17 +11,18 @@
"./gmi-web.1" "./gmi-web.1"
], ],
"files": [ "files": [
"UNLICENSE",
"README.md",
"CONTRIBUTING.md",
"gmi-web.1",
"gmi-web.css",
"gmi.css",
"html.js", "html.js",
"html.spec.js", "html.spec.js",
"css.js", "css.js",
"css.spec.js", "css.spec.js",
"cli.js" "gmi.css",
"gmi-web.css",
"cli.js",
"README.md",
"CONTRIBUTING.md",
"gmi-web.1",
"example.gmi",
"UNLICENSE"
], ],
"scripts": { "scripts": {
"test": "NODE_OPTIONS=--experimental-vm-modules jest", "test": "NODE_OPTIONS=--experimental-vm-modules jest",