This commit is contained in:
Talon Poole 2021-01-28 23:07:57 +00:00
parent 69d524e79e
commit e587d1a933
7 changed files with 73 additions and 29 deletions

1
.gitignore vendored
View file

@ -1,3 +1,4 @@
*.min.* *.min.*
node_modules/ node_modules/
gmi-web.1 gmi-web.1
capsule/

View file

@ -75,19 +75,17 @@ gmi.css will respect system dark mode preferences by inverting `--foreground` an
## gmi-web(1) ## gmi-web(1)
``` ```
gmi-web [--out path] [--no-css] [files] gmi-web [--no-css] [files..]
A bridge between Gemini and HTML. See gmi-web(1) for more details. Convert .gmi to .html. See gmi-web(1) for more details.
Positionals: Positionals:
files .gmi files to convert to .html files .gmi files to convert to .html [required]
Options: Options:
--version Show version number --version Show version number [boolean]
--help Show help --help Show help [boolean]
--out Write .html files to a seperate directory located at path. --css Toggle inclusion of gmi.css. [boolean] [default: true]
--no-css Do not include gmi.css in the rendered HTML markup.
``` ```
*You will need*: *You will need*:

19
cli.js
View file

@ -3,28 +3,29 @@ const path = require("path");
const fs = require("vinyl-fs"); const fs = require("vinyl-fs");
const map = require("map-stream"); const map = require("map-stream");
const tokenize = require("./tokenize"); const tokenize = require("./tokenize");
const toHTML = require("./to-html");
require("yargs") require("yargs")
.scriptName("gmi-web") .scriptName("gmi-web")
.command( .command(
"$0 [--no-css] [files..]", "$0 [--no-css] [files..]",
"A bridge between Gemini and HTML. See gmi-web(1) for more details.", "Convert .gmi to .html. See gmi-web(1) for more details.",
(yargs) => (yargs) =>
yargs yargs
.positional("files", { .positional("files", {
describe: ".gmi files to convert to .html", describe: ".gmi files to convert to .html",
}) })
.required("files", true) .required("files", true)
.option("no-css", { .option("css", {
type: "boolean", type: "boolean",
description: "Do not include gmi.css in the rendered HTML markup.", default: true,
description: "Toggle inclusion of gmi.css.",
}), }),
(argv) => { (argv) => {
fs.src(argv.files).pipe(map(tokenize)).pipe(map(log)); fs.src(argv.files)
//.pipe(fs.dest((file) => { .pipe(map(tokenize))
// const dest = path.dirname(file.path) .pipe(map(toHTML({css: argv["css"]})))
// console.log(file.path, dest) .pipe(fs.dest((file) => path.dirname(file.path)));
// return dest
//}));
} }
) )
.help().argv; .help().argv;

View file

@ -6,7 +6,7 @@ gmi-web - A bridge between Gemini and HTML
# SYNOPSIS # SYNOPSIS
*gmi-web* [--out _path_] [--no-css] _files_ *gmi-web* [--no-css] _files_
# DESCRIPTION # DESCRIPTION
@ -15,9 +15,6 @@ and mobile-friendly fashion!
# OPTIONS # OPTIONS
*--out* _path_
Write .html files to a seperate directory located at _path_.
*--no-css* *--no-css*
Do not include gmi.css in the rendered HTML markup. Do not include gmi.css in the rendered HTML markup.
@ -29,12 +26,6 @@ The following example will create .html converted files next to their .gmi count
$ gmi-web gmi/*.gmi $ gmi-web gmi/*.gmi
``` ```
If you would like to seperate the .html files out to their own folder use the *--out* flag.
```
$ gmi-web --out html gmi/*.gmi
```
# AUTHORS # AUTHORS
Maintained by Talon Poole <code@talon.computer>. Up-to-date sources can be Maintained by Talon Poole <code@talon.computer>. Up-to-date sources can be

View file

@ -1 +1 @@
module.exports = /^((=>\s?(?<href>[^\s]+)\s(?<title>.+))|(?<pre>```\s?(?<alt>.+)?)|(###\s?(?<h3>.+))|(##\s?(?<h2>.+))|(#\s?(?<h1>.+))|(\*\s?(?<li>.+))|(>\s?(?<quote>.+))|(?<text>(.+)?))$/; module.exports = /^((=>\s?(?<href>[^\s]+)(\s(?<title>.+))?)|(?<pre>```\s?(?<alt>.+)?)|(###\s?(?<h3>.+))|(##\s?(?<h2>.+))|(#\s?(?<h1>.+))|(\*\s?(?<li>.+))|(>\s?(?<quote>.+))|(?<text>(.+)?))$/;

54
to-html.js Normal file
View file

@ -0,0 +1,54 @@
const TOKENS_EXT = /\.tokens\.json$/;
module.exports = ({css} = {css: true}) => (file, cb) => {
if (!TOKENS_EXT.test(file.path)) return cb(null, file);
file.path = file.path.replace(TOKENS_EXT, ".html");
file.contents = Buffer.from(`<!DOCTYPE html>
<html>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">${css ?
'\n<meta name="color-scheme" content="dark light">\n<link rel="stylesheet" href="/gmi.min.css">'
: ''}
<body>
${toHTML(JSON.parse(file.contents.toString("utf8")))}
</body>
</html>
`);
return cb(null, file);
};
function toHTML(tokens) {
let body = []
let cursor = tokens.shift()
while (tokens.length) {
if (cursor.pre) {
body.push(`<pre${cursor.alt ? `title="${cursor.alt}"` : ""}>`)
const closing = tokens.findIndex(token => token.pre)
body = body.concat(tokens.slice(0, closing).map(({text}) => text))
body.push("</pre>")
tokens = tokens.slice(closing + 1)
}
if (cursor.li) {
body.push(`<ul>`)
const closing = tokens.findIndex(token => !token.li)
body = body.concat(tokens.slice(0, closing).map(line))
body.push("</ul>")
tokens = tokens.slice(closing + 1)
}
body.push(line(cursor))
cursor = tokens.shift()
}
return body.join("\n")
}
function line({ text, href, title, pre, alt, h1, h2, h3, li, quote }) {
if (text) return `<p>${text}</p>`;
if (href) return `<a href="${href}">${title || href}</a>`;
if (h1) return `<h1>${h1}</h1>`;
if (h2) return `<h2>${h2}</h2>`;
if (h3) return `<h3>${h3}</h3>`;
if (li) return `<li>${li}</li>`;
if (quote) return `<blockquote>${quote}</blockquote>`;
return `<p><br></p>`;
}

View file

@ -4,7 +4,6 @@ module.exports = (file, cb) => {
if (!GMI_EXT.test(file.path)) return cb(null, file); if (!GMI_EXT.test(file.path)) return cb(null, file);
file.path = file.path.replace(GMI_EXT, ".tokens.json"); file.path = file.path.replace(GMI_EXT, ".tokens.json");
console.log(`tokenizing ${file.path}`);
file.contents = Buffer.from( file.contents = Buffer.from(
JSON.stringify( JSON.stringify(
file.contents file.contents