refactor
This commit is contained in:
parent
b1d2f7140b
commit
e5bb1fa77c
57
cli.js
57
cli.js
|
@ -2,10 +2,9 @@
|
|||
import { readFileSync, existsSync } from "fs";
|
||||
import path from "path";
|
||||
import fs from "vinyl-fs";
|
||||
import map from "map-stream";
|
||||
import yargs from "yargs";
|
||||
import * as CSS from "./css.js";
|
||||
import toHTML, { html } from "./html.js";
|
||||
import { streamHTML, toHTML } from "./html.js";
|
||||
|
||||
const cli = yargs(process.argv.slice(2))
|
||||
.config("config", function (path) {
|
||||
|
@ -47,12 +46,6 @@ const cli = yargs(process.argv.slice(2))
|
|||
default: "full",
|
||||
requiresArg: true,
|
||||
},
|
||||
charset: {
|
||||
type: "string",
|
||||
hidden: true,
|
||||
default: "utf-8",
|
||||
requiresArg: true,
|
||||
},
|
||||
image: {
|
||||
type: "array",
|
||||
requiresArg: true,
|
||||
|
@ -65,37 +58,37 @@ const cli = yargs(process.argv.slice(2))
|
|||
type: "array",
|
||||
requiresArg: true,
|
||||
},
|
||||
charset: {
|
||||
type: "string",
|
||||
hidden: true,
|
||||
default: "utf-8",
|
||||
requiresArg: true,
|
||||
},
|
||||
modes: {
|
||||
type: "boolean",
|
||||
hidden: true,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const CSS_VARS = CSS.rootVariables(CSS.FULL);
|
||||
Object.keys(CSS_VARS).map((key) => {
|
||||
cli.option(key, { default: CSS_VARS[key] });
|
||||
cli.option(key, "core");
|
||||
cli.option(key, "none");
|
||||
return key;
|
||||
});
|
||||
cli.group(Object.keys(CSS_VARS), "CSS:");
|
||||
|
||||
const argv = cli
|
||||
.conflicts("author", "body")
|
||||
.conflicts("descriptions", "body")
|
||||
.conflicts("html", "body")
|
||||
.group(["html", "body"], "Core:")
|
||||
.group(["author", "descriptions", "css", "dir"], "HTML:")
|
||||
.group(["author", "descriptions", "css", "mode", "dir"], "HTML:")
|
||||
.group(["image", "audio", "video"], "Inline Media:")
|
||||
.group(Object.keys(CSS_VARS), "CSS:")
|
||||
.alias("html", "language")
|
||||
.alias("html", "lang")
|
||||
.coerce("css", (arg) => {
|
||||
if (arg === "full") {
|
||||
return CSS.stringify(CSS.FULL, { compress: true });
|
||||
} else if (arg === "core") {
|
||||
return CSS.stringify(CSS.CORE, { compress: true });
|
||||
} else if (arg !== "none" && arg !== "") {
|
||||
if (existsSync(path.resolve(arg))) {
|
||||
return CSS.resolve(arg, { compress: true });
|
||||
} else {
|
||||
return CSS.parse(arg);
|
||||
}
|
||||
}
|
||||
})
|
||||
.showHelpOnFail(true)
|
||||
.help().argv;
|
||||
|
||||
|
@ -105,13 +98,6 @@ if (!argv.html && !argv.body) {
|
|||
cli.exit(1);
|
||||
}
|
||||
|
||||
let styles = Object.keys(CSS_VARS).reduce((style, key) => {
|
||||
if (argv[key]) {
|
||||
style += `--${key}: ${argv[key]};`;
|
||||
}
|
||||
return style;
|
||||
}, "");
|
||||
|
||||
if (!argv.files) {
|
||||
let gemtext;
|
||||
try {
|
||||
|
@ -121,16 +107,9 @@ if (!argv.files) {
|
|||
console.error("\nMissing files: pipe from stdin or provide [files..]");
|
||||
cli.exit(1);
|
||||
}
|
||||
console.log(html({ contents: gemtext }, argv));
|
||||
console.log(toHTML(gemtext, argv));
|
||||
} else {
|
||||
fs.src(argv.files)
|
||||
.pipe(
|
||||
map(
|
||||
toHTML({
|
||||
...argv,
|
||||
styles,
|
||||
})
|
||||
)
|
||||
)
|
||||
.pipe(streamHTML(argv))
|
||||
.pipe(fs.dest((file) => path.dirname(file.path)));
|
||||
}
|
||||
|
|
21
gmi-web.1
21
gmi-web.1
|
@ -26,15 +26,9 @@ mobile-friendly fashion!
|
|||
.P
|
||||
\fB--body\fR
|
||||
.RS 4
|
||||
Generate only the HTML for the lines of the Gemini document.
|
||||
Generate just the HTML for the lines of the Gemini document.
|
||||
.P
|
||||
.RE
|
||||
.nf
|
||||
.RS 4
|
||||
gmi-web --body < doc\&.gmi
|
||||
.fi
|
||||
.RE
|
||||
.P
|
||||
\fB--html\fR \fILANG\fR
|
||||
.RS 4
|
||||
Generate a full HTML5 document with the provided \fILANG\fR. \fB--dir\fR can be used
|
||||
|
@ -48,10 +42,10 @@ be used.
|
|||
Use \fB--author\fR \fINAME\fR to set the author <meta> tag on every file.
|
||||
.P
|
||||
.RE
|
||||
\fB--css\fR [\fIMODE\fR|\fIFILE\fR|\fICSS\fR]
|
||||
\fB--css\fR [\fIMODE\fR|\fIFILE\fR]
|
||||
.RS 4
|
||||
By default this will be set to \fBfull\fR enabling a handful of customizable
|
||||
variables to be set. See --help for the complete list.
|
||||
variables. See \fB--help\fR for the complete list.
|
||||
.P
|
||||
.RE
|
||||
.nf
|
||||
|
@ -64,12 +58,11 @@ gmi-web --html en \\
|
|||
.P
|
||||
.RS 4
|
||||
Choosing \fBcore\fR will use just what is needed to fix vertical layout issues
|
||||
with CSS 2.1's Normal Flow and inline elements.
|
||||
with CSS 2.1's Normal Flow and inline elements. Pointing to a .css \fIFILE\fR
|
||||
will use those styles.
|
||||
.P
|
||||
Pointing to a .css \fIFILE\fR or providing a valid \fICSS\fR string will use those
|
||||
styles.
|
||||
.P
|
||||
Choosing \fBnone\fR will not include any style information.
|
||||
Choosing \fBnone\fR will not include any style information including when paired
|
||||
with \fB--body\fR where it will NOT apply the core inline styles.
|
||||
.P
|
||||
.RE
|
||||
\fB[--image|--audio|--video]\fR \fIEXTENSIONS\fR
|
||||
|
|
|
@ -18,8 +18,7 @@ mobile-friendly fashion!
|
|||
# OPTIONS
|
||||
|
||||
*--body*
|
||||
Generate only the HTML for the lines of the Gemini document. Use *--css* none
|
||||
to turn off the core inline styles.
|
||||
Generate just the HTML for the lines of the Gemini document.
|
||||
|
||||
*--html* _LANG_
|
||||
Generate a full HTML5 document with the provided _LANG_. *--dir* can be used
|
||||
|
@ -32,9 +31,9 @@ mobile-friendly fashion!
|
|||
|
||||
Use *--author* _NAME_ to set the author <meta> tag on every file.
|
||||
|
||||
*--css* [_MODE_|_FILE_|_CSS_]
|
||||
*--css* [_MODE_|_FILE_]
|
||||
By default this will be set to *full* enabling a handful of customizable
|
||||
variables to be set. See --help for the complete list.
|
||||
variables. See *--help* for the complete list.
|
||||
|
||||
```
|
||||
gmi-web --html en \\
|
||||
|
@ -43,12 +42,11 @@ gmi-web --html en \\
|
|||
```
|
||||
|
||||
Choosing *core* will use just what is needed to fix vertical layout issues
|
||||
with CSS 2.1's Normal Flow and inline elements.
|
||||
with CSS 2.1's Normal Flow and inline elements. Pointing to a .css _FILE_
|
||||
will use those styles.
|
||||
|
||||
Pointing to a .css _FILE_ or providing a valid _CSS_ string will use those
|
||||
styles.
|
||||
|
||||
Choosing *none* will not include any style information.
|
||||
Choosing *none* will not include any style information including when paired
|
||||
with *--body* where it will NOT apply the core inline styles.
|
||||
|
||||
*[--image|--audio|--video]* _EXTENSIONS_
|
||||
Include media extensions inline. You can provide multiple extensions per flag
|
||||
|
|
60
html.js
60
html.js
|
@ -1,14 +1,15 @@
|
|||
import fs from "fs";
|
||||
import map from "map-stream";
|
||||
import escape from "escape-html";
|
||||
import * as CSS from "./css.js";
|
||||
|
||||
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) {
|
||||
const tokens = file.contents
|
||||
.toString("utf8")
|
||||
.split("\n")
|
||||
.map((line) => GMI_REGEX.exec(line).groups);
|
||||
|
||||
export function toHTML(gemtext, options) {
|
||||
const tokens = gemtext.split("\n").map((line) => GMI_REGEX.exec(line).groups);
|
||||
|
||||
let description = options.descriptions
|
||||
? tokens.find((token) => {
|
||||
|
@ -36,11 +37,12 @@ ${body(tokens, options)}
|
|||
export function head(options) {
|
||||
return `
|
||||
<meta charset="${options.charset}">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">${
|
||||
options.css && options.css !== ""
|
||||
? `<meta name="color-scheme" content="dark light">`
|
||||
: ""
|
||||
}${options.css && options.css !== "" ? `<style>${options.css}</style>` : ""}
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
${
|
||||
options.modes || options.css === "full"
|
||||
? `<meta name="color-scheme" content="dark light">\n`
|
||||
: ""
|
||||
}${style(options.css)}
|
||||
<title>${options.title}</title>${
|
||||
!options.author ? "" : `<meta name="author" content="${options.author}">`
|
||||
}${
|
||||
|
@ -55,6 +57,19 @@ export function head(options) {
|
|||
`;
|
||||
}
|
||||
|
||||
export function style(mode) {
|
||||
if (mode === "none") return "";
|
||||
if (mode === "core")
|
||||
return `<style>${CSS.stringify(CSS.CORE, { compress: true })}</style>`;
|
||||
if (mode === "full")
|
||||
return `<style>${CSS.stringify(CSS.FULL, { compress: true })}</style>`;
|
||||
if (fs.existsSync(mode)) {
|
||||
return `<style>${CSS.resolve(mode, { compress: true })}</style>`;
|
||||
} else {
|
||||
throw new Error(`Cannot find file ${mode}.`);
|
||||
}
|
||||
}
|
||||
|
||||
function line(
|
||||
{ text, href, title, pre, alt, h1, h2, h3, li, quote },
|
||||
{ image, audio, video, css, body } = {}
|
||||
|
@ -63,7 +78,8 @@ function line(
|
|||
|
||||
if (href) {
|
||||
const titleProp = title ? ` title="${title}"` : "";
|
||||
const FIX_NORMAL_FLOW = body && css ? ` style="display: block; max-width: 100%;"` : ""
|
||||
const FIX_NORMAL_FLOW =
|
||||
body && css !== "none" ? ` style="display: block; max-width: 100%;"` : "";
|
||||
const matchesExt = (url, exts) =>
|
||||
exts.some((ext) => new RegExp(`\.${ext}$`).test(url));
|
||||
|
||||
|
@ -77,7 +93,9 @@ function line(
|
|||
return `<video controls src="${href}"${titleProp}${FIX_NORMAL_FLOW}/></video>`;
|
||||
}
|
||||
|
||||
return `<a href="${href}"${FIX_NORMAL_FLOW}>${title ? escape(title) : href}</a>`;
|
||||
return `<a href="${href}"${FIX_NORMAL_FLOW}>${
|
||||
title ? escape(title) : href
|
||||
}</a>`;
|
||||
}
|
||||
|
||||
if (h1) return `<h1>${escape(h1)}</h1>`;
|
||||
|
@ -120,10 +138,14 @@ export function body(tokens, options) {
|
|||
|
||||
export const GMI_EXT = /\.gmi$/;
|
||||
|
||||
export default (options) => (file, cb) => {
|
||||
if (!GMI_EXT.test(file.path)) return cb(null);
|
||||
file.contents = Buffer.from(html(file, options));
|
||||
file.path = file.path.replace(GMI_EXT, ".html");
|
||||
if (options.verbose) console.log(file.path);
|
||||
return cb(null, file);
|
||||
};
|
||||
export function streamHTML(options) {
|
||||
return map((file, cb) => {
|
||||
if (!GMI_EXT.test(file.path)) return cb(null);
|
||||
file.contents = Buffer.from(
|
||||
toHTML(file.contents.toString("utf-8"), options)
|
||||
);
|
||||
file.path = file.path.replace(GMI_EXT, ".html");
|
||||
if (options.verbose) console.log(file.path);
|
||||
return cb(null, file);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue