#!/usr/bin/env node 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"; const cli = yargs(process.argv.slice(2)) .config("config", function (path) { return JSON.parse(readFileSync(path), "utf-8"); }) .scriptName("gmi-web") .command("$0 [files..]", "Convert text/gemini to text/html.", (yargs) => yargs .example("$0 --body < ~/my-capsule/index.gmi") .example("$0 --html en $(find ~/my-capsule -name '*.gmi')") .example( "$0 --foreground '#000000' --background '#EEEEEE' --html en < doc.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, }, dir: { type: "string", choices: ["rtl", "ltr"], default: "ltr", requiresArg: true, }, author: { type: "string", requiresArg: true, }, descriptions: { type: "boolean", }, css: { default: "full", requiresArg: true, }, charset: { type: "string", hidden: true, default: "utf-8", requiresArg: true, }, image: { type: "array", requiresArg: true, }, audio: { type: "array", requiresArg: true, }, video: { type: "array", requiresArg: true, }, }); const CSS_VARS = CSS.rootVariables(CSS.FULL); Object.keys(CSS_VARS).map((key) => { cli.option(key, { default: CSS_VARS[key] }); return key; }); const argv = cli .conflicts("author", "body") .conflicts("descriptions", "body") .conflicts("html", "body") .group(["html", "body"], "Core:") .group(["author", "descriptions", "css", "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; if (!argv.html && !argv.body) { cli.showHelp(); console.error(`\nMissing required argument: --html or --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 { gemtext = readFileSync(process.stdin.fd, "utf-8"); } 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))); }