#!/usr/bin/env node import { readFileSync } from "fs"; import path from "path"; import fs from "vinyl-fs"; import map from "map-stream"; import yargs from "yargs"; import CleanCSS from "clean-css"; import toHTML, { BASE_CSS, html } from "./html.js"; const cli = yargs(process.argv.slice(2)) .scriptName("gmi-web") .command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) => yargs .example("$0 --html en $(find ~/my-capsule -name '*.gmi')") .example("$0 --foreground '#000000' --background '#EEEEEE' --html en < doc.gmi") .example("$0 --body < ~/my-capsule/index.gmi") .example("$0 --image jpg --audio mp3 --image png --body < doc.gmi") .epilog("See the gmi-web(1) man page for more information") ) .options({ body: { type: "boolean", }, html: { type: "string", requiresArg: true, }, charset: { type: "string", default: "utf-8", requiresArg: true, }, css: { choices: ["gmi.css", "base", "none"], default: "gmi.css", }, }); cli.options({ image: { type: "array", requiresArg: true, }, audio: { type: "array", requiresArg: true, }, video: { type: "array", requiresArg: true, }, }); // TODO: automatically pull these in from gmi.css (also for gmi.css(5)) const GMI_CSS_VARS = [ "body-width", "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", ].map((key) => { cli.option(key); cli.conflicts(key, "body"); return key; }); const argv = cli .conflicts("html", "body") .group(["html", "css", "body"], "Core:") .group(GMI_CSS_VARS, "gmi.css:") .group(["image", "audio", "video"], "Inline Media:") .alias("html", "language") .alias("html", "lang") .config() .showHelpOnFail(true) .help().argv; let styles = ""; if (argv.css === "gmi.css") { styles = GMI_CSS_VARS.reduce((style, key) => { if (argv[key]) { style += `--${key}: ${argv[key]};`; } }, 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" ), "utf-8" ) ).styles; } 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); } if (!argv.files) { let gemtext; try { gemtext = readFileSync(process.stdin.fd); } catch (e) { cli.showHelp(); console.error("\nMissing files: pipe from stdin or provide [files..]"); cli.exit(1); } console.log(html({ contents: gemtext }, argv)); } else { fs.src(argv.files) .pipe( map( toHTML({ ...argv, styles, }) ) ) .pipe(fs.dest((file) => path.dirname(file.path))); }