You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
readability-cli/index.js

164 lines
3.5 KiB
JavaScript

#!/usr/bin/env node
const Readability = require('readability');
const JSDOM = require('jsdom').JSDOM;
const parseArgs = require('minimist');
const fs = require('fs');
const ExitCodes = {
4 years ago
badUsageCLI: 64,
dataError: 65,
noInput: 66,
noHost: 68,
noPermission: 77
};
let errored = false;
4 years ago
function setErrored(exitCode) {
process.exitCode = exitCode;
errored = true;
}
function printUsage() {
console.error(`
Usage:
readable [SOURCE] [options]
readable [options] -- [SOURCE]
(where SOURCE is a file, an http(s) URL, or '-' for standard input)
Options:
--help Print help
-o --output OUTPUT_FILE Output to OUTPUT_FILE`);
}
const stringArgParams = ['_', '--', "output"];
const boolArgParams = ["help"];
const alias = {
"output": 'o',
}
let args = parseArgs(process.argv.slice(2), {
"string": stringArgParams,
"boolean": boolArgParams,
"alias": alias,
"--": true
});
//Minimist's parseArgs accepts a function for handling unknown parameters,
//but it works in a stupid way, so I'm writing my own.
for (var key of Object.keys(args)) {
if (!stringArgParams.includes(key) && !boolArgParams.includes(key) &&
!Object.values(alias).includes(key)) {
console.error(`Unknown argument: ${key}`);
4 years ago
setErrored(ExitCodes.badUsageCLI);
} else if (stringArgParams.includes(key) && args[key] === "") {
console.error(`Error: no value given for --${key}`);
4 years ago
setErrored(ExitCodes.badUsageCLI);
}
}
if (errored) {
printUsage();
return;
}
if (args.help) {
printUsage();
return;
}
let inputArg;
const inputCount = args['_'].length + args['--'].length;
if (inputCount > 1) {
console.error("Too many input arguments");
printUsage();
4 years ago
setErrored(ExitCodes.badUsageCLI);
return;
} else if (inputCount == 0) {
if (process.stdin.isTTY) {
console.error("No input provided");
printUsage();
4 years ago
setErrored(ExitCodes.badUsageCLI);
return;
} else {
inputArg = '-'
}
} else {
inputArg = (args['_'].length > 0) ? args['_'][0] : args['--'][0];
}
//Get input parameter, remove inputArg from args
let inputFile;
let inputURL;
let inputIsFromStdin = false;
if (inputArg.startsWith("https://") || inputArg.startsWith("http://"))
inputURL = inputArg;
else if (inputArg == '-')
inputIsFromStdin = true;
else
inputFile = inputArg;
delete args['_'];
delete args['--'];
const outputArg = args['output'];
if (inputIsFromStdin) {
onLoadDOM(new JSDOM(fs.readFileSync(0, 'utf-8')));
} else {
let promiseGetHTML;
if (inputURL)
promiseGetHTML = JSDOM.fromURL(inputURL);
else if (inputFile)
promiseGetHTML = JSDOM.fromFile(inputFile);
4 years ago
promiseGetHTML.then(onLoadDOM)
.catch(onLoadDOMError);
}
function onLoadDOM(dom) {
let reader = new Readability(dom.window.document);
let article = reader.parse();
4 years ago
if (!article) {
console.error("Couldn't parse document");
setErrored(ExitCodes.dataError);
return;
}
if (outputArg) {
fs.writeFileSync(outputArg, article.content);
} else {
console.log(article.content);
}
// console.log(chalk.blue(article.textContent));
}
4 years ago
function onLoadDOMError(error) {
if (error.code == "ENOENT") {
console.error(error.message);
setErrored(ExitCodes.noInput);
} else if (error.code ="EACCES") {
console.error(error.message);
setErrored(ExitCodes.noPermission);
} else if (error.error && error.error.code == "ENOTFOUND") {
console.error(`Host not found: '${error.hostname}'`);
setErrored(ExitCodes.noHost);
} else if (error.statusCode) {
console.error(`Status error: ${error.response.statusMessage}`);
setErrored(ExitCodes.noHost);
} else {
console.error(error);
}
}