From dd6df6dd6dbda5c76da939f362df0d34880fcdbc Mon Sep 17 00:00:00 2001 From: mitchell Date: Sun, 14 Jun 2026 15:39:41 -0400 Subject: [PATCH] refactor(tts): CLI argument parsing and input handling --- tts.ts | 52 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/tts.ts b/tts.ts index 7bf3ceb..559ecf6 100644 --- a/tts.ts +++ b/tts.ts @@ -12,41 +12,34 @@ import { execa } from "execa"; import { Window } from "happy-dom"; import wrapAnsi from "wrap-ansi"; +class InputError extends Error { + override name = "InputError"; +} + async function main() { const start = Date.now(); - const { values: args } = util.parseArgs({ + const { values: args, positionals } = util.parseArgs({ args: process.argv.slice(2), options: { wait: { type: "boolean" }, w: { type: "boolean" }, - url: { type: "string" }, - u: { type: "string" }, + url: { type: "boolean" }, + u: { type: "boolean" }, edit: { type: "boolean" }, e: { type: "boolean" }, }, strict: true, - allowPositionals: false, + allowPositionals: true, }); const wait = args.wait ?? args.w; const url = args.url ?? args.u; const edit = args.edit ?? args.e; - let input = ""; + let input = positionals.join(" ").trim(); - if (url?.trim()) { - const result = await fetch(url.trim()); - const window = new Window({ url: url.trim() }); - const doc = window.document; - doc.write(await result.text()); - await window.happyDOM.waitUntilComplete(); - const readable = new Readability(doc).parse(); - if (!readable?.textContent) { - throw "Unable to read web page."; - } - input = readable.textContent.trim(); - } else { + if (!input) { process.stdin.setEncoding("utf8"); process.stdin.on("data", (data) => { input += data; @@ -55,7 +48,24 @@ async function main() { } if (!input) { - throw "No input given."; + throw new InputError( + "No input was given. Use positional arguments or stdin.", + ); + } + + if (url) { + const result = await fetch(input); + const window = new Window({ url: input }); + const doc = window.document; + doc.write(await result.text()); + await window.happyDOM.waitUntilComplete(); + const readable = new Readability(doc).parse(); + if (!readable?.textContent) { + throw new InputError( + "Unable to read webpage. This usually means it's blocking scrapers.", + ); + } + input = readable.textContent.trim(); } const home = os.homedir(); @@ -63,7 +73,7 @@ async function main() { if (edit) { const file = path.join(cwd, "input.txt"); - await fs.writeFile(file, input); + await fs.writeFile(file, input, "utf8"); const editor = process.env.EDITOR || "nano"; await execa({ cwd, @@ -154,11 +164,11 @@ async function main() { } catch (err) { if (err instanceof Object && "code" in err && err.code === "ENOENT") { console.error("Sox not found. Unable to combine outputs."); + console.log(chalk.yellow(`Audio available in ${cwd}`)); + return; } else { throw err; } - console.log(chalk.yellow(`Audio available in ${cwd}`)); - return; } const [columns] = process.stdout.getWindowSize();