add --author and --descriptions options

This commit is contained in:
Talon Poole 2021-02-12 21:59:18 +00:00
parent 45e90e61ac
commit 5254445e77
4 changed files with 51 additions and 9 deletions

23
cli.js
View file

@ -9,7 +9,7 @@ import toHTML, { BASE_CSS, html } from "./html.js";
const cli = yargs(process.argv.slice(2)) const cli = yargs(process.argv.slice(2))
.config("config", function (path) { .config("config", function (path) {
return JSON.parse(readFileSync(path)) return JSON.parse(readFileSync(path));
}) })
.scriptName("gmi-web") .scriptName("gmi-web")
.command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) => .command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) =>
@ -30,15 +30,23 @@ const cli = yargs(process.argv.slice(2))
type: "string", type: "string",
requiresArg: true, requiresArg: true,
}, },
charset: { author: {
type: "string", type: "string",
default: "utf-8",
requiresArg: true, requiresArg: true,
}, },
descriptions: {
type: "boolean",
},
css: { css: {
choices: ["gmi.css", "base", "none"], choices: ["gmi.css", "base", "none"],
default: "gmi.css", default: "gmi.css",
}, },
charset: {
type: "string",
hidden: true,
default: "utf-8",
requiresArg: true,
},
}); });
cli.options({ cli.options({
@ -85,10 +93,13 @@ const GMI_CSS_VARS = [
}); });
const argv = cli const argv = cli
.conflicts("author", "body")
.conflicts("description", "body")
.conflicts("html", "body") .conflicts("html", "body")
.group(["html", "css", "body"], "Core:") .group(["html", "body"], "Core:")
.group(GMI_CSS_VARS, "gmi.css:") .group(["author", "description", "css"], "HTML:")
.group(["image", "audio", "video"], "Inline Media:") .group(["image", "audio", "video"], "Inline Media:")
.group(GMI_CSS_VARS, "gmi.css:")
.alias("html", "language") .alias("html", "language")
.alias("html", "lang") .alias("html", "lang")
.showHelpOnFail(true) .showHelpOnFail(true)
@ -100,7 +111,7 @@ if (argv.css === "gmi.css") {
if (argv[key]) { if (argv[key]) {
style += `--${key}: ${argv[key]};`; style += `--${key}: ${argv[key]};`;
} }
return style return style;
}, styles); }, styles);
argv.css = new CleanCSS().minify( argv.css = new CleanCSS().minify(

View file

@ -31,6 +31,18 @@ Generate a full HTML5 document with the provided \fILANG\fR.
\fBgmi-web\fR --html en < doc.gmi \fBgmi-web\fR --html en < doc.gmi
.P .P
.RE .RE
\fB--descriptions\fR
.RS 4
If this flag is set the first non-empty text line of each Gemini file will
be used for the description <meta> tag. This will be truncated to 200
characters using an ellipsis.
.P
.RE
\fB--author\fR
.RS 4
If provided this will be used for the author <meta> tag on every file.
.P
.RE
\fB--body\fR \fB--body\fR
.RS 4 .RS 4
Generate only the HTML for the lines of the Gemini document. Generate only the HTML for the lines of the Gemini document.

View file

@ -22,6 +22,14 @@ and mobile-friendly fashion!
*gmi-web* --html en < doc.gmi *gmi-web* --html en < doc.gmi
*--descriptions*
If this flag is set the first non-empty text line of each Gemini file will
be used for the description <meta> tag. This will be truncated to 200
characters using an ellipsis.
*--author*
If provided this will be used for the author <meta> tag on every file.
*--body* *--body*
Generate only the HTML for the lines of the Gemini document. Generate only the HTML for the lines of the Gemini document.

17
html.js
View file

@ -2,12 +2,20 @@ import escape from "escape-html";
export const GMI_REGEX = /^((=>\s?(?<href>[^\s]+)(\s(?<title>.+))?)|(?<pre>```\s?(?<alt>.+)?)|(###\s?(?<h3>.+))|(##\s?(?<h2>.+))|(#\s?(?<h1>.+))|(\*\s?(?<li>.+))|(>\s?(?<quote>.+))|(?<text>(.+)?))$/; export const GMI_REGEX = /^((=>\s?(?<href>[^\s]+)(\s(?<title>.+))?)|(?<pre>```\s?(?<alt>.+)?)|(###\s?(?<h3>.+))|(##\s?(?<h2>.+))|(#\s?(?<h1>.+))|(\*\s?(?<li>.+))|(>\s?(?<quote>.+))|(?<text>(.+)?))$/;
const truncate = (text, limit) =>
text.length > limit ? `${text.substring(0, limit)}...` : text;
export function html(file, options) { export function html(file, options) {
const tokens = file.contents const tokens = file.contents
.toString("utf8") .toString("utf8")
.split("\n") .split("\n")
.map((line) => GMI_REGEX.exec(line).groups); .map((line) => GMI_REGEX.exec(line).groups);
let description = options.descriptions
? tokens.find((token) => {
return token.text && token.text !== "";
})
: false;
if (options.body) return body(tokens, options); if (options.body) return body(tokens, options);
return `<!DOCTYPE html> return `<!DOCTYPE html>
@ -15,7 +23,7 @@ export function html(file, options) {
<head>${head( <head>${head(
Object.assign(options, { Object.assign(options, {
title: tokens[0].h1, title: tokens[0].h1,
// TODO: description, canonical description: description && truncate(description.text, 200),
}) })
)}</head> )}</head>
<body> <body>
@ -27,6 +35,7 @@ ${body(tokens, options)}
export const BASE_CSS = export const BASE_CSS =
"p,a,pre,h1,h2,h3,ul,blockquote,img,audio,video{display:block;max-width:100%;margin:0;padding:0;overflow-wrap:anywhere;}"; "p,a,pre,h1,h2,h3,ul,blockquote,img,audio,video{display:block;max-width:100%;margin:0;padding:0;overflow-wrap:anywhere;}";
export function head(options) { export function head(options) {
return ` return `
<meta charset="${options.charset}"> <meta charset="${options.charset}">
@ -34,13 +43,15 @@ export function head(options) {
${options.css !== "" ? `<meta name="color-scheme" content="dark light">` : ""} ${options.css !== "" ? `<meta name="color-scheme" content="dark light">` : ""}
${options.css !== "" ? `<style>${options.css}</style>` : ""} ${options.css !== "" ? `<style>${options.css}</style>` : ""}
<title>${options.title}</title>${ <title>${options.title}</title>${
!options.author ? "" : `<meta name="author" content="${options.author}">`
}${
!options.description !options.description
? "" ? ""
: `<meta name="description" content="${options.description}}">` : `<meta name="description" content="${escape(options.description)}">`
}${ }${
!options.canonical !options.canonical
? "" ? ""
: `<link rel="canonical" href="${options.canonical}}">` : `<link rel="canonical" href="${options.canonical}">`
} }
`; `;
} }