2021-01-28 06:16:46 +00:00
|
|
|
#!/usr/bin/env node
|
2021-02-11 20:57:08 +00:00
|
|
|
import { readFileSync } from "fs";
|
2021-02-10 22:03:48 +00:00
|
|
|
import path from "path";
|
|
|
|
import fs from "vinyl-fs";
|
|
|
|
import map from "map-stream";
|
|
|
|
import yargs from "yargs";
|
2021-02-11 20:57:08 +00:00
|
|
|
import CleanCSS from "clean-css";
|
2021-02-11 23:26:29 +00:00
|
|
|
import toHTML, { BASE_CSS, html } from "./html.js";
|
2021-02-11 20:57:08 +00:00
|
|
|
|
|
|
|
const cli = yargs(process.argv.slice(2))
|
2021-02-12 21:17:00 +00:00
|
|
|
.config("config", function (path) {
|
2021-02-12 21:59:18 +00:00
|
|
|
return JSON.parse(readFileSync(path));
|
2021-02-12 21:17:00 +00:00
|
|
|
})
|
2021-02-11 20:57:08 +00:00
|
|
|
.scriptName("gmi-web")
|
2021-02-11 23:26:29 +00:00
|
|
|
.command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) =>
|
|
|
|
yargs
|
|
|
|
.example("$0 --html en $(find ~/my-capsule -name '*.gmi')")
|
2021-02-12 20:41:43 +00:00
|
|
|
.example(
|
|
|
|
"$0 --foreground '#000000' --background '#EEEEEE' --html en < doc.gmi"
|
|
|
|
)
|
2021-02-11 23:26:29 +00:00
|
|
|
.example("$0 --body < ~/my-capsule/index.gmi")
|
2021-02-11 23:49:39 +00:00
|
|
|
.example("$0 --image jpg --audio mp3 --image png --body < doc.gmi")
|
|
|
|
.epilog("See the gmi-web(1) man page for more information")
|
2021-02-11 20:57:08 +00:00
|
|
|
)
|
|
|
|
.options({
|
2021-02-11 23:49:39 +00:00
|
|
|
body: {
|
|
|
|
type: "boolean",
|
|
|
|
},
|
2021-02-11 23:26:29 +00:00
|
|
|
html: {
|
|
|
|
type: "string",
|
|
|
|
requiresArg: true,
|
2021-02-11 20:57:08 +00:00
|
|
|
},
|
2021-02-12 21:59:18 +00:00
|
|
|
author: {
|
2021-02-12 01:59:02 +00:00
|
|
|
type: "string",
|
|
|
|
requiresArg: true,
|
|
|
|
},
|
2021-02-12 21:59:18 +00:00
|
|
|
descriptions: {
|
|
|
|
type: "boolean",
|
|
|
|
},
|
2021-02-11 20:57:08 +00:00
|
|
|
css: {
|
2021-02-11 23:26:29 +00:00
|
|
|
choices: ["gmi.css", "base", "none"],
|
|
|
|
default: "gmi.css",
|
|
|
|
},
|
2021-02-12 21:59:18 +00:00
|
|
|
charset: {
|
|
|
|
type: "string",
|
|
|
|
hidden: true,
|
|
|
|
default: "utf-8",
|
|
|
|
requiresArg: true,
|
|
|
|
},
|
2021-02-11 20:57:08 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
cli.options({
|
2021-02-11 23:26:29 +00:00
|
|
|
image: {
|
|
|
|
type: "array",
|
2021-02-11 23:49:39 +00:00
|
|
|
requiresArg: true,
|
2021-02-11 20:57:08 +00:00
|
|
|
},
|
|
|
|
audio: {
|
2021-02-11 23:26:29 +00:00
|
|
|
type: "array",
|
2021-02-11 23:49:39 +00:00
|
|
|
requiresArg: true,
|
2021-02-11 20:57:08 +00:00
|
|
|
},
|
|
|
|
video: {
|
2021-02-11 23:26:29 +00:00
|
|
|
type: "array",
|
2021-02-11 23:49:39 +00:00
|
|
|
requiresArg: true,
|
2021-02-11 20:57:08 +00:00
|
|
|
},
|
|
|
|
});
|
2021-01-28 23:07:57 +00:00
|
|
|
|
2021-01-29 22:32:30 +00:00
|
|
|
// TODO: automatically pull these in from gmi.css (also for gmi.css(5))
|
|
|
|
const GMI_CSS_VARS = [
|
2021-02-11 20:57:08 +00:00
|
|
|
"body-width",
|
2021-01-29 22:32:30 +00:00
|
|
|
"foreground",
|
|
|
|
"background",
|
|
|
|
"p-size",
|
|
|
|
"p-indent",
|
|
|
|
"p-line-height",
|
|
|
|
"a-size",
|
|
|
|
"pre-size",
|
|
|
|
"pre-line-height",
|
|
|
|
"h1-size",
|
|
|
|
"h2-size",
|
|
|
|
"h3-size",
|
|
|
|
"heading-line-height",
|
|
|
|
"ul-size",
|
|
|
|
"ul-line-height",
|
|
|
|
"blockquote-size",
|
|
|
|
"blockquote-line-height",
|
|
|
|
"mono",
|
|
|
|
"serif",
|
|
|
|
"sans-serif",
|
2021-02-11 20:57:08 +00:00
|
|
|
].map((key) => {
|
|
|
|
cli.option(key);
|
2021-02-11 23:26:29 +00:00
|
|
|
cli.conflicts(key, "body");
|
2021-02-11 20:57:08 +00:00
|
|
|
return key;
|
|
|
|
});
|
2021-01-29 22:32:30 +00:00
|
|
|
|
2021-02-11 20:57:08 +00:00
|
|
|
const argv = cli
|
2021-02-12 21:59:18 +00:00
|
|
|
.conflicts("author", "body")
|
|
|
|
.conflicts("description", "body")
|
2021-02-11 23:26:29 +00:00
|
|
|
.conflicts("html", "body")
|
2021-02-12 21:59:18 +00:00
|
|
|
.group(["html", "body"], "Core:")
|
|
|
|
.group(["author", "description", "css"], "HTML:")
|
2021-02-11 23:49:39 +00:00
|
|
|
.group(["image", "audio", "video"], "Inline Media:")
|
2021-02-12 21:59:18 +00:00
|
|
|
.group(GMI_CSS_VARS, "gmi.css:")
|
2021-02-11 23:26:29 +00:00
|
|
|
.alias("html", "language")
|
|
|
|
.alias("html", "lang")
|
2021-02-11 20:57:08 +00:00
|
|
|
.showHelpOnFail(true)
|
2021-01-28 06:16:46 +00:00
|
|
|
.help().argv;
|
2021-02-11 20:57:08 +00:00
|
|
|
|
|
|
|
let styles = "";
|
2021-02-11 23:26:29 +00:00
|
|
|
if (argv.css === "gmi.css") {
|
2021-02-11 20:57:08 +00:00
|
|
|
styles = GMI_CSS_VARS.reduce((style, key) => {
|
|
|
|
if (argv[key]) {
|
|
|
|
style += `--${key}: ${argv[key]};`;
|
|
|
|
}
|
2021-02-12 21:59:18 +00:00
|
|
|
return style;
|
2021-02-11 20:57:08 +00:00
|
|
|
}, styles);
|
|
|
|
|
|
|
|
argv.css = new CleanCSS().minify(
|
|
|
|
readFileSync(
|
|
|
|
// TODO import.meta.resolve is supposed to accomplish this without "path"
|
|
|
|
path.resolve(
|
|
|
|
path.dirname(new URL(import.meta.url).pathname),
|
|
|
|
"./gmi.css"
|
|
|
|
),
|
2021-02-12 01:59:02 +00:00
|
|
|
"utf-8"
|
2021-02-11 20:57:08 +00:00
|
|
|
)
|
|
|
|
).styles;
|
2021-02-11 23:26:29 +00:00
|
|
|
} else if (argv.css === "base") {
|
|
|
|
argv.css = BASE_CSS;
|
|
|
|
} else {
|
|
|
|
argv.css = "";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!argv.lang && !argv.body) {
|
|
|
|
cli.showHelp();
|
|
|
|
console.error(`\nMissing required argument: --html or --body`);
|
|
|
|
cli.exit(1);
|
2021-02-11 20:57:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!argv.files) {
|
2021-02-11 23:26:29 +00:00
|
|
|
let gemtext;
|
2021-02-11 20:57:08 +00:00
|
|
|
try {
|
2021-02-11 23:26:29 +00:00
|
|
|
gemtext = readFileSync(process.stdin.fd);
|
2021-02-11 20:57:08 +00:00
|
|
|
} catch (e) {
|
2021-02-12 01:59:02 +00:00
|
|
|
cli.showHelp();
|
|
|
|
console.error("\nMissing files: pipe from stdin or provide [files..]");
|
2021-02-11 23:26:29 +00:00
|
|
|
cli.exit(1);
|
2021-02-11 20:57:08 +00:00
|
|
|
}
|
2021-02-11 23:26:29 +00:00
|
|
|
console.log(html({ contents: gemtext }, argv));
|
2021-02-11 20:57:08 +00:00
|
|
|
} else {
|
|
|
|
fs.src(argv.files)
|
|
|
|
.pipe(
|
|
|
|
map(
|
|
|
|
toHTML({
|
|
|
|
...argv,
|
|
|
|
styles,
|
|
|
|
})
|
|
|
|
)
|
|
|
|
)
|
|
|
|
.pipe(fs.dest((file) => path.dirname(file.path)));
|
|
|
|
}
|