UNPKG

tweet-to-markdown

Version:

Quickly save Tweets as Markdown files.

1,750 lines (1,597 loc) 1.43 MB
#!/usr/bin/env node 'use strict'; var os = require('os'); var tty = require('tty'); var http = require('http'); var https = require('https'); var url = require('url'); var require$$0$3 = require('stream'); var assert = require('assert'); var util$1 = require('util'); var zlib = require('zlib'); var fs = require('fs'); var path = require('path'); var cp = require('child_process'); var require$$2 = require('events'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var os__default = /*#__PURE__*/_interopDefaultLegacy(os); var tty__default = /*#__PURE__*/_interopDefaultLegacy(tty); var http__default = /*#__PURE__*/_interopDefaultLegacy(http); var https__default = /*#__PURE__*/_interopDefaultLegacy(https); var url__default = /*#__PURE__*/_interopDefaultLegacy(url); var require$$0__default = /*#__PURE__*/_interopDefaultLegacy(require$$0$3); var assert__default = /*#__PURE__*/_interopDefaultLegacy(assert); var util__default = /*#__PURE__*/_interopDefaultLegacy(util$1); var zlib__default = /*#__PURE__*/_interopDefaultLegacy(zlib); var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs); var path__default = /*#__PURE__*/_interopDefaultLegacy(path); var cp__default = /*#__PURE__*/_interopDefaultLegacy(cp); var require$$2__default = /*#__PURE__*/_interopDefaultLegacy(require$$2); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ function __awaiter(thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); } var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn) { var module = { exports: {} }; return fn(module, module.exports), module.exports; } /** * lodash (Custom Build) <https://lodash.com/> * Build: `lodash modularize exports="npm" -o ./` * Copyright jQuery Foundation and other contributors <https://jquery.org/> * Released under MIT license <https://lodash.com/license> * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE> * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors */ /** Used as references for various `Number` constants. */ var INFINITY = 1 / 0; /** `Object#toString` result references. */ var symbolTag = '[object Symbol]'; /** Used to match words composed of alphanumeric characters. */ var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; /** Used to match Latin Unicode letters (excluding mathematical operators). */ var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; /** Used to compose unicode character classes. */ var rsAstralRange = '\\ud800-\\udfff', rsComboMarksRange = '\\u0300-\\u036f\\ufe20-\\ufe23', rsComboSymbolsRange = '\\u20d0-\\u20f0', rsDingbatRange = '\\u2700-\\u27bf', rsLowerRange = 'a-z\\xdf-\\xf6\\xf8-\\xff', rsMathOpRange = '\\xac\\xb1\\xd7\\xf7', rsNonCharRange = '\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf', rsPunctuationRange = '\\u2000-\\u206f', rsSpaceRange = ' \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000', rsUpperRange = 'A-Z\\xc0-\\xd6\\xd8-\\xde', rsVarRange = '\\ufe0e\\ufe0f', rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange; /** Used to compose unicode capture groups. */ var rsApos = "['\u2019]", rsAstral = '[' + rsAstralRange + ']', rsBreak = '[' + rsBreakRange + ']', rsCombo = '[' + rsComboMarksRange + rsComboSymbolsRange + ']', rsDigits = '\\d+', rsDingbat = '[' + rsDingbatRange + ']', rsLower = '[' + rsLowerRange + ']', rsMisc = '[^' + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + ']', rsFitz = '\\ud83c[\\udffb-\\udfff]', rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')', rsNonAstral = '[^' + rsAstralRange + ']', rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}', rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]', rsUpper = '[' + rsUpperRange + ']', rsZWJ = '\\u200d'; /** Used to compose unicode regexes. */ var rsLowerMisc = '(?:' + rsLower + '|' + rsMisc + ')', rsUpperMisc = '(?:' + rsUpper + '|' + rsMisc + ')', rsOptLowerContr = '(?:' + rsApos + '(?:d|ll|m|re|s|t|ve))?', rsOptUpperContr = '(?:' + rsApos + '(?:D|LL|M|RE|S|T|VE))?', reOptMod = rsModifier + '?', rsOptVar = '[' + rsVarRange + ']?', rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*', rsSeq = rsOptVar + reOptMod + rsOptJoin, rsEmoji = '(?:' + [rsDingbat, rsRegional, rsSurrPair].join('|') + ')' + rsSeq, rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')'; /** Used to match apostrophes. */ var reApos = RegExp(rsApos, 'g'); /** * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). */ var reComboMark = RegExp(rsCombo, 'g'); /** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */ var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g'); /** Used to match complex or compound words. */ var reUnicodeWord = RegExp([ rsUpper + '?' + rsLower + '+' + rsOptLowerContr + '(?=' + [rsBreak, rsUpper, '$'].join('|') + ')', rsUpperMisc + '+' + rsOptUpperContr + '(?=' + [rsBreak, rsUpper + rsLowerMisc, '$'].join('|') + ')', rsUpper + '?' + rsLowerMisc + '+' + rsOptLowerContr, rsUpper + '+' + rsOptUpperContr, rsDigits, rsEmoji ].join('|'), 'g'); /** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */ var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange + rsComboMarksRange + rsComboSymbolsRange + rsVarRange + ']'); /** Used to detect strings that need a more robust regexp to match words. */ var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/; /** Used to map Latin Unicode letters to basic Latin letters. */ var deburredLetters = { // Latin-1 Supplement block. '\xc0': 'A', '\xc1': 'A', '\xc2': 'A', '\xc3': 'A', '\xc4': 'A', '\xc5': 'A', '\xe0': 'a', '\xe1': 'a', '\xe2': 'a', '\xe3': 'a', '\xe4': 'a', '\xe5': 'a', '\xc7': 'C', '\xe7': 'c', '\xd0': 'D', '\xf0': 'd', '\xc8': 'E', '\xc9': 'E', '\xca': 'E', '\xcb': 'E', '\xe8': 'e', '\xe9': 'e', '\xea': 'e', '\xeb': 'e', '\xcc': 'I', '\xcd': 'I', '\xce': 'I', '\xcf': 'I', '\xec': 'i', '\xed': 'i', '\xee': 'i', '\xef': 'i', '\xd1': 'N', '\xf1': 'n', '\xd2': 'O', '\xd3': 'O', '\xd4': 'O', '\xd5': 'O', '\xd6': 'O', '\xd8': 'O', '\xf2': 'o', '\xf3': 'o', '\xf4': 'o', '\xf5': 'o', '\xf6': 'o', '\xf8': 'o', '\xd9': 'U', '\xda': 'U', '\xdb': 'U', '\xdc': 'U', '\xf9': 'u', '\xfa': 'u', '\xfb': 'u', '\xfc': 'u', '\xdd': 'Y', '\xfd': 'y', '\xff': 'y', '\xc6': 'Ae', '\xe6': 'ae', '\xde': 'Th', '\xfe': 'th', '\xdf': 'ss', // Latin Extended-A block. '\u0100': 'A', '\u0102': 'A', '\u0104': 'A', '\u0101': 'a', '\u0103': 'a', '\u0105': 'a', '\u0106': 'C', '\u0108': 'C', '\u010a': 'C', '\u010c': 'C', '\u0107': 'c', '\u0109': 'c', '\u010b': 'c', '\u010d': 'c', '\u010e': 'D', '\u0110': 'D', '\u010f': 'd', '\u0111': 'd', '\u0112': 'E', '\u0114': 'E', '\u0116': 'E', '\u0118': 'E', '\u011a': 'E', '\u0113': 'e', '\u0115': 'e', '\u0117': 'e', '\u0119': 'e', '\u011b': 'e', '\u011c': 'G', '\u011e': 'G', '\u0120': 'G', '\u0122': 'G', '\u011d': 'g', '\u011f': 'g', '\u0121': 'g', '\u0123': 'g', '\u0124': 'H', '\u0126': 'H', '\u0125': 'h', '\u0127': 'h', '\u0128': 'I', '\u012a': 'I', '\u012c': 'I', '\u012e': 'I', '\u0130': 'I', '\u0129': 'i', '\u012b': 'i', '\u012d': 'i', '\u012f': 'i', '\u0131': 'i', '\u0134': 'J', '\u0135': 'j', '\u0136': 'K', '\u0137': 'k', '\u0138': 'k', '\u0139': 'L', '\u013b': 'L', '\u013d': 'L', '\u013f': 'L', '\u0141': 'L', '\u013a': 'l', '\u013c': 'l', '\u013e': 'l', '\u0140': 'l', '\u0142': 'l', '\u0143': 'N', '\u0145': 'N', '\u0147': 'N', '\u014a': 'N', '\u0144': 'n', '\u0146': 'n', '\u0148': 'n', '\u014b': 'n', '\u014c': 'O', '\u014e': 'O', '\u0150': 'O', '\u014d': 'o', '\u014f': 'o', '\u0151': 'o', '\u0154': 'R', '\u0156': 'R', '\u0158': 'R', '\u0155': 'r', '\u0157': 'r', '\u0159': 'r', '\u015a': 'S', '\u015c': 'S', '\u015e': 'S', '\u0160': 'S', '\u015b': 's', '\u015d': 's', '\u015f': 's', '\u0161': 's', '\u0162': 'T', '\u0164': 'T', '\u0166': 'T', '\u0163': 't', '\u0165': 't', '\u0167': 't', '\u0168': 'U', '\u016a': 'U', '\u016c': 'U', '\u016e': 'U', '\u0170': 'U', '\u0172': 'U', '\u0169': 'u', '\u016b': 'u', '\u016d': 'u', '\u016f': 'u', '\u0171': 'u', '\u0173': 'u', '\u0174': 'W', '\u0175': 'w', '\u0176': 'Y', '\u0177': 'y', '\u0178': 'Y', '\u0179': 'Z', '\u017b': 'Z', '\u017d': 'Z', '\u017a': 'z', '\u017c': 'z', '\u017e': 'z', '\u0132': 'IJ', '\u0133': 'ij', '\u0152': 'Oe', '\u0153': 'oe', '\u0149': "'n", '\u017f': 'ss' }; /** Detect free variable `global` from Node.js. */ var freeGlobal = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal; /** Detect free variable `self`. */ var freeSelf = typeof self == 'object' && self && self.Object === Object && self; /** Used as a reference to the global object. */ var root = freeGlobal || freeSelf || Function('return this')(); /** * A specialized version of `_.reduce` for arrays without support for * iteratee shorthands. * * @private * @param {Array} [array] The array to iterate over. * @param {Function} iteratee The function invoked per iteration. * @param {*} [accumulator] The initial value. * @param {boolean} [initAccum] Specify using the first element of `array` as * the initial value. * @returns {*} Returns the accumulated value. */ function arrayReduce(array, iteratee, accumulator, initAccum) { var index = -1, length = array ? array.length : 0; if (initAccum && length) { accumulator = array[++index]; } while (++index < length) { accumulator = iteratee(accumulator, array[index], index, array); } return accumulator; } /** * Converts an ASCII `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function asciiToArray(string) { return string.split(''); } /** * Splits an ASCII `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function asciiWords(string) { return string.match(reAsciiWord) || []; } /** * The base implementation of `_.propertyOf` without support for deep paths. * * @private * @param {Object} object The object to query. * @returns {Function} Returns the new accessor function. */ function basePropertyOf(object) { return function(key) { return object == null ? undefined : object[key]; }; } /** * Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A * letters to basic Latin letters. * * @private * @param {string} letter The matched letter to deburr. * @returns {string} Returns the deburred letter. */ var deburrLetter = basePropertyOf(deburredLetters); /** * Checks if `string` contains Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a symbol is found, else `false`. */ function hasUnicode(string) { return reHasUnicode.test(string); } /** * Checks if `string` contains a word composed of Unicode symbols. * * @private * @param {string} string The string to inspect. * @returns {boolean} Returns `true` if a word is found, else `false`. */ function hasUnicodeWord(string) { return reHasUnicodeWord.test(string); } /** * Converts `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function stringToArray(string) { return hasUnicode(string) ? unicodeToArray(string) : asciiToArray(string); } /** * Converts a Unicode `string` to an array. * * @private * @param {string} string The string to convert. * @returns {Array} Returns the converted array. */ function unicodeToArray(string) { return string.match(reUnicode) || []; } /** * Splits a Unicode `string` into an array of its words. * * @private * @param {string} The string to inspect. * @returns {Array} Returns the words of `string`. */ function unicodeWords(string) { return string.match(reUnicodeWord) || []; } /** Used for built-in method references. */ var objectProto = Object.prototype; /** * Used to resolve the * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring) * of values. */ var objectToString$1 = objectProto.toString; /** Built-in value references. */ var Symbol$1 = root.Symbol; /** Used to convert symbols to primitives and strings. */ var symbolProto = Symbol$1 ? Symbol$1.prototype : undefined, symbolToString = symbolProto ? symbolProto.toString : undefined; /** * The base implementation of `_.slice` without an iteratee call guard. * * @private * @param {Array} array The array to slice. * @param {number} [start=0] The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the slice of `array`. */ function baseSlice(array, start, end) { var index = -1, length = array.length; if (start < 0) { start = -start > length ? 0 : (length + start); } end = end > length ? length : end; if (end < 0) { end += length; } length = start > end ? 0 : ((end - start) >>> 0); start >>>= 0; var result = Array(length); while (++index < length) { result[index] = array[index + start]; } return result; } /** * The base implementation of `_.toString` which doesn't convert nullish * values to empty strings. * * @private * @param {*} value The value to process. * @returns {string} Returns the string. */ function baseToString(value) { // Exit early for strings to avoid a performance hit in some environments. if (typeof value == 'string') { return value; } if (isSymbol$2(value)) { return symbolToString ? symbolToString.call(value) : ''; } var result = (value + ''); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } /** * Casts `array` to a slice if it's needed. * * @private * @param {Array} array The array to inspect. * @param {number} start The start position. * @param {number} [end=array.length] The end position. * @returns {Array} Returns the cast slice. */ function castSlice(array, start, end) { var length = array.length; end = end === undefined ? length : end; return (!start && end >= length) ? array : baseSlice(array, start, end); } /** * Creates a function like `_.lowerFirst`. * * @private * @param {string} methodName The name of the `String` case method to use. * @returns {Function} Returns the new case function. */ function createCaseFirst(methodName) { return function(string) { string = toString$1(string); var strSymbols = hasUnicode(string) ? stringToArray(string) : undefined; var chr = strSymbols ? strSymbols[0] : string.charAt(0); var trailing = strSymbols ? castSlice(strSymbols, 1).join('') : string.slice(1); return chr[methodName]() + trailing; }; } /** * Creates a function like `_.camelCase`. * * @private * @param {Function} callback The function to combine each word. * @returns {Function} Returns the new compounder function. */ function createCompounder(callback) { return function(string) { return arrayReduce(words(deburr(string).replace(reApos, '')), callback, ''); }; } /** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * _.isObjectLike({}); * // => true * * _.isObjectLike([1, 2, 3]); * // => true * * _.isObjectLike(_.noop); * // => false * * _.isObjectLike(null); * // => false */ function isObjectLike(value) { return !!value && typeof value == 'object'; } /** * Checks if `value` is classified as a `Symbol` primitive or object. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. * @example * * _.isSymbol(Symbol.iterator); * // => true * * _.isSymbol('abc'); * // => false */ function isSymbol$2(value) { return typeof value == 'symbol' || (isObjectLike(value) && objectToString$1.call(value) == symbolTag); } /** * Converts `value` to a string. An empty string is returned for `null` * and `undefined` values. The sign of `-0` is preserved. * * @static * @memberOf _ * @since 4.0.0 * @category Lang * @param {*} value The value to process. * @returns {string} Returns the string. * @example * * _.toString(null); * // => '' * * _.toString(-0); * // => '-0' * * _.toString([1, 2, 3]); * // => '1,2,3' */ function toString$1(value) { return value == null ? '' : baseToString(value); } /** * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the camel cased string. * @example * * _.camelCase('Foo Bar'); * // => 'fooBar' * * _.camelCase('--foo-bar--'); * // => 'fooBar' * * _.camelCase('__FOO_BAR__'); * // => 'fooBar' */ var camelCase$1 = createCompounder(function(result, word, index) { word = word.toLowerCase(); return result + (index ? capitalize(word) : word); }); /** * Converts the first character of `string` to upper case and the remaining * to lower case. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to capitalize. * @returns {string} Returns the capitalized string. * @example * * _.capitalize('FRED'); * // => 'Fred' */ function capitalize(string) { return upperFirst(toString$1(string).toLowerCase()); } /** * Deburrs `string` by converting * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) * letters to basic Latin letters and removing * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to deburr. * @returns {string} Returns the deburred string. * @example * * _.deburr('déjà vu'); * // => 'deja vu' */ function deburr(string) { string = toString$1(string); return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); } /** * Converts the first character of `string` to upper case. * * @static * @memberOf _ * @since 4.0.0 * @category String * @param {string} [string=''] The string to convert. * @returns {string} Returns the converted string. * @example * * _.upperFirst('fred'); * // => 'Fred' * * _.upperFirst('FRED'); * // => 'FRED' */ var upperFirst = createCaseFirst('toUpperCase'); /** * Splits `string` into an array of its words. * * @static * @memberOf _ * @since 3.0.0 * @category String * @param {string} [string=''] The string to inspect. * @param {RegExp|string} [pattern] The pattern to match words. * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`. * @returns {Array} Returns the words of `string`. * @example * * _.words('fred, barney, & pebbles'); * // => ['fred', 'barney', 'pebbles'] * * _.words('fred, barney, & pebbles', /[^, ]+/g); * // => ['fred', 'barney', '&', 'pebbles'] */ function words(string, pattern, guard) { string = toString$1(string); pattern = guard ? undefined : pattern; if (pattern === undefined) { return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string); } return string.match(pattern) || []; } var lodash_camelcase = camelCase$1; function _interopDefault$1 (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var camelCase = _interopDefault$1(lodash_camelcase); /** * Takes any input and guarantees an array back. * * - Converts array-like objects (e.g. `arguments`, `Set`) to a real array. * - Converts `undefined` to an empty array. * - Converts any another other, singular value (including `null`, objects and iterables other than `Set`) into an array containing that value. * - Ignores input which is already an array. * * @module array-back * @example * > const arrayify = require('array-back') * * > arrayify(undefined) * [] * * > arrayify(null) * [ null ] * * > arrayify(0) * [ 0 ] * * > arrayify([ 1, 2 ]) * [ 1, 2 ] * * > arrayify(new Set([ 1, 2 ])) * [ 1, 2 ] * * > function f(){ return arrayify(arguments); } * > f(1,2,3) * [ 1, 2, 3 ] */ function isObject$1 (input) { return typeof input === 'object' && input !== null } function isArrayLike (input) { return isObject$1(input) && typeof input.length === 'number' } /** * @param {*} - The input value to convert to an array * @returns {Array} * @alias module:array-back */ function arrayify (input) { if (Array.isArray(input)) { return input } if (input === undefined) { return [] } if (isArrayLike(input) || input instanceof Set) { return Array.from(input) } return [ input ] } /** * Takes any input and guarantees an array back. * * - converts array-like objects (e.g. `arguments`) to a real array * - converts `undefined` to an empty array * - converts any another other, singular value (including `null`) into an array containing that value * - ignores input which is already an array * * @module array-back * @example * > const arrayify = require('array-back') * * > arrayify(undefined) * [] * * > arrayify(null) * [ null ] * * > arrayify(0) * [ 0 ] * * > arrayify([ 1, 2 ]) * [ 1, 2 ] * * > function f(){ return arrayify(arguments); } * > f(1,2,3) * [ 1, 2, 3 ] */ function isObject$1$1 (input) { return typeof input === 'object' && input !== null } function isArrayLike$1 (input) { return isObject$1$1(input) && typeof input.length === 'number' } /** * @param {*} - the input value to convert to an array * @returns {Array} * @alias module:array-back */ function arrayify$1 (input) { if (Array.isArray(input)) { return input } else { if (input === undefined) { return [] } else if (isArrayLike$1(input)) { return Array.prototype.slice.call(input) } else { return [ input ] } } } /** * Find and either replace or remove items in an array. * * @module find-replace * @example * > const findReplace = require('find-replace') * > const numbers = [ 1, 2, 3] * * > findReplace(numbers, n => n === 2, 'two') * [ 1, 'two', 3 ] * * > findReplace(numbers, n => n === 2, [ 'two', 'zwei' ]) * [ 1, [ 'two', 'zwei' ], 3 ] * * > findReplace(numbers, n => n === 2, 'two', 'zwei') * [ 1, 'two', 'zwei', 3 ] * * > findReplace(numbers, n => n === 2) // no replacement, so remove * [ 1, 3 ] */ /** * @param {array} - The input array * @param {testFn} - A predicate function which, if returning `true` causes the current item to be operated on. * @param [replaceWith] {...any} - If specified, found values will be replaced with these values, else removed. * @returns {array} * @alias module:find-replace */ function findReplace (array, testFn) { const found = []; const replaceWiths = arrayify$1(arguments); replaceWiths.splice(0, 2); arrayify$1(array).forEach((value, index) => { let expanded = []; replaceWiths.forEach(replaceWith => { if (typeof replaceWith === 'function') { expanded = expanded.concat(replaceWith(value)); } else { expanded.push(replaceWith); } }); if (testFn(value)) { found.push({ index: index, replaceWithValue: expanded }); } }); found.reverse().forEach(item => { const spliceArgs = [ item.index, 1 ].concat(item.replaceWithValue); array.splice.apply(array, spliceArgs); }); return array } /** * Some useful tools for working with `process.argv`. * * @module argv-tools * @typicalName argvTools * @example * const argvTools = require('argv-tools') */ /** * Regular expressions for matching option formats. * @static */ const re$1 = { short: /^-([^\d-])$/, long: /^--(\S+)/, combinedShort: /^-[^\d-]{2,}$/, optEquals: /^(--\S+?)=(.*)/ }; /** * Array subclass encapsulating common operations on `process.argv`. * @static */ class ArgvArray extends Array { /** * Clears the array has loads the supplied input. * @param {string[]} argv - The argv list to load. Defaults to `process.argv`. */ load (argv) { this.clear(); if (argv && argv !== process.argv) { argv = arrayify(argv); } else { /* if no argv supplied, assume we are parsing process.argv */ argv = process.argv.slice(0); const deleteCount = process.execArgv.some(isExecArg) ? 1 : 2; argv.splice(0, deleteCount); } argv.forEach(arg => this.push(String(arg))); } /** * Clear the array. */ clear () { this.length = 0; } /** * expand ``--option=value` style args. */ expandOptionEqualsNotation () { if (this.some(arg => re$1.optEquals.test(arg))) { const expandedArgs = []; this.forEach(arg => { const matches = arg.match(re$1.optEquals); if (matches) { expandedArgs.push(matches[1], matches[2]); } else { expandedArgs.push(arg); } }); this.clear(); this.load(expandedArgs); } } /** * expand getopt-style combinedShort options. */ expandGetoptNotation () { if (this.hasCombinedShortOptions()) { findReplace(this, re$1.combinedShort, expandCombinedShortArg); } } /** * Returns true if the array contains combined short options (e.g. `-ab`). * @returns {boolean} */ hasCombinedShortOptions () { return this.some(arg => re$1.combinedShort.test(arg)) } static from (argv) { const result = new this(); result.load(argv); return result } } /** * Expand a combined short option. * @param {string} - the string to expand, e.g. `-ab` * @returns {string[]} * @static */ function expandCombinedShortArg (arg) { /* remove initial hypen */ arg = arg.slice(1); return arg.split('').map(letter => '-' + letter) } /** * Returns true if the supplied arg matches `--option=value` notation. * @param {string} - the arg to test, e.g. `--one=something` * @returns {boolean} * @static */ function isOptionEqualsNotation (arg) { return re$1.optEquals.test(arg) } /** * Returns true if the supplied arg is in either long (`--one`) or short (`-o`) format. * @param {string} - the arg to test, e.g. `--one` * @returns {boolean} * @static */ function isOption (arg) { return (re$1.short.test(arg) || re$1.long.test(arg)) && !re$1.optEquals.test(arg) } /** * Returns true if the supplied arg is in long (`--one`) format. * @param {string} - the arg to test, e.g. `--one` * @returns {boolean} * @static */ function isLongOption (arg) { return re$1.long.test(arg) && !isOptionEqualsNotation(arg) } /** * Returns the name from a long, short or `--options=value` arg. * @param {string} - the arg to inspect, e.g. `--one` * @returns {string} * @static */ function getOptionName (arg) { if (re$1.short.test(arg)) { return arg.match(re$1.short)[1] } else if (isLongOption(arg)) { return arg.match(re$1.long)[1] } else if (isOptionEqualsNotation(arg)) { return arg.match(re$1.optEquals)[1].replace(/^--/, '') } else { return null } } function isValue (arg) { return !(isOption(arg) || re$1.combinedShort.test(arg) || re$1.optEquals.test(arg)) } function isExecArg (arg) { return ['--eval', '-e'].indexOf(arg) > -1 || arg.startsWith('--eval=') } /** * For type-checking Javascript values. * @module typical * @typicalname t * @example * const t = require('typical') */ /** * Returns true if input is a number * @param {*} - the input to test * @returns {boolean} * @static * @example * > t.isNumber(0) * true * > t.isNumber(1) * true * > t.isNumber(1.1) * true * > t.isNumber(0xff) * true * > t.isNumber(0644) * true * > t.isNumber(6.2e5) * true * > t.isNumber(NaN) * false * > t.isNumber(Infinity) * false */ function isNumber$2 (n) { return !isNaN(parseFloat(n)) && isFinite(n) } /** * A plain object is a simple object literal, it is not an instance of a class. Returns true if the input `typeof` is `object` and directly decends from `Object`. * * @param {*} - the input to test * @returns {boolean} * @static * @example * > t.isPlainObject({ something: 'one' }) * true * > t.isPlainObject(new Date()) * false * > t.isPlainObject([ 0, 1 ]) * false * > t.isPlainObject(/test/) * false * > t.isPlainObject(1) * false * > t.isPlainObject('one') * false * > t.isPlainObject(null) * false * > t.isPlainObject((function * () {})()) * false * > t.isPlainObject(function * () {}) * false */ function isPlainObject$1 (input) { return input !== null && typeof input === 'object' && input.constructor === Object } /** * An array-like value has all the properties of an array, but is not an array instance. Examples in the `arguments` object. Returns true if the input value is an object, not null and has a `length` property with a numeric value. * * @param {*} - the input to test * @returns {boolean} * @static * @example * function sum(x, y){ * console.log(t.isArrayLike(arguments)) * // prints `true` * } */ function isArrayLike$2 (input) { return isObject$2(input) && typeof input.length === 'number' } /** * returns true if the typeof input is `'object'`, but not null! * @param {*} - the input to test * @returns {boolean} * @static */ function isObject$2 (input) { return typeof input === 'object' && input !== null } /** * Returns true if the input value is defined * @param {*} - the input to test * @returns {boolean} * @static */ function isDefined (input) { return typeof input !== 'undefined' } /** * Returns true if the input value is a string * @param {*} - the input to test * @returns {boolean} * @static */ function isString$2 (input) { return typeof input === 'string' } /** * Returns true if the input value is a boolean * @param {*} - the input to test * @returns {boolean} * @static */ function isBoolean$1 (input) { return typeof input === 'boolean' } /** * Returns true if the input value is a function * @param {*} - the input to test * @returns {boolean} * @static */ function isFunction$2 (input) { return typeof input === 'function' } /** * Returns true if the input value is an es2015 `class`. * @param {*} - the input to test * @returns {boolean} * @static */ function isClass (input) { if (isFunction$2(input)) { return /^class /.test(Function.prototype.toString.call(input)) } else { return false } } /** * Returns true if the input is a string, number, symbol, boolean, null or undefined value. * @param {*} - the input to test * @returns {boolean} * @static */ function isPrimitive$3 (input) { if (input === null) return true switch (typeof input) { case 'string': case 'number': case 'symbol': case 'undefined': case 'boolean': return true default: return false } } /** * Returns true if the input is a Promise. * @param {*} - the input to test * @returns {boolean} * @static */ function isPromise (input) { if (input) { const isPromise = isDefined(Promise) && input instanceof Promise; const isThenable = input.then && typeof input.then === 'function'; return !!(isPromise || isThenable) } else { return false } } /** * Returns true if the input is an iterable (`Map`, `Set`, `Array`, Generator etc.). * @param {*} - the input to test * @returns {boolean} * @static * @example * > t.isIterable('string') * true * > t.isIterable(new Map()) * true * > t.isIterable([]) * true * > t.isIterable((function * () {})()) * true * > t.isIterable(Promise.resolve()) * false * > t.isIterable(Promise) * false * > t.isIterable(true) * false * > t.isIterable({}) * false * > t.isIterable(0) * false * > t.isIterable(1.1) * false * > t.isIterable(NaN) * false * > t.isIterable(Infinity) * false * > t.isIterable(function () {}) * false * > t.isIterable(Date) * false * > t.isIterable() * false * > t.isIterable({ then: function () {} }) * false */ function isIterable (input) { if (input === null || !isDefined(input)) { return false } else { return ( typeof input[Symbol.iterator] === 'function' || typeof input[Symbol.asyncIterator] === 'function' ) } } var t = { isNumber: isNumber$2, isString: isString$2, isBoolean: isBoolean$1, isPlainObject: isPlainObject$1, isArrayLike: isArrayLike$2, isObject: isObject$2, isDefined, isFunction: isFunction$2, isClass, isPrimitive: isPrimitive$3, isPromise, isIterable }; /** * @module option-definition */ /** * Describes a command-line option. Additionally, if generating a usage guide with [command-line-usage](https://github.com/75lb/command-line-usage) you could optionally add `description` and `typeLabel` properties to each definition. * * @alias module:option-definition * @typicalname option */ class OptionDefinition { constructor (definition) { /** * The only required definition property is `name`, so the simplest working example is * ```js * const optionDefinitions = [ * { name: 'file' }, * { name: 'depth' } * ] * ``` * * Where a `type` property is not specified it will default to `String`. * * | # | argv input | commandLineArgs() output | * | --- | -------------------- | ------------ | * | 1 | `--file` | `{ file: null }` | * | 2 | `--file lib.js` | `{ file: 'lib.js' }` | * | 3 | `--depth 2` | `{ depth: '2' }` | * * Unicode option names and aliases are valid, for example: * ```js * const optionDefinitions = [ * { name: 'один' }, * { name: '两' }, * { name: 'три', alias: 'т' } * ] * ``` * @type {string} */ this.name = definition.name; /** * The `type` value is a setter function (you receive the output from this), enabling you to be specific about the type and value received. * * The most common values used are `String` (the default), `Number` and `Boolean` but you can use a custom function, for example: * * ```js * const fs = require('fs') * * class FileDetails { * constructor (filename) { * this.filename = filename * this.exists = fs.existsSync(filename) * } * } * * const cli = commandLineArgs([ * { name: 'file', type: filename => new FileDetails(filename) }, * { name: 'depth', type: Number } * ]) * ``` * * | # | argv input | commandLineArgs() output | * | --- | ----------------- | ------------ | * | 1 | `--file asdf.txt` | `{ file: { filename: 'asdf.txt', exists: false } }` | * * The `--depth` option expects a `Number`. If no value was set, you will receive `null`. * * | # | argv input | commandLineArgs() output | * | --- | ----------------- | ------------ | * | 2 | `--depth` | `{ depth: null }` | * | 3 | `--depth 2` | `{ depth: 2 }` | * * @type {function} * @default String */ this.type = definition.type || String; /** * getopt-style short option names. Can be any single character (unicode included) except a digit or hyphen. * * ```js * const optionDefinitions = [ * { name: 'hot', alias: 'h', type: Boolean }, * { name: 'discount', alias: 'd', type: Boolean }, * { name: 'courses', alias: 'c' , type: Number } * ] * ``` * * | # | argv input | commandLineArgs() output | * | --- | ------------ | ------------ | * | 1 | `-hcd` | `{ hot: true, courses: null, discount: true }` | * | 2 | `-hdc 3` | `{ hot: true, discount: true, courses: 3 }` | * * @type {string} */ this.alias = definition.alias; /** * Set this flag if the option takes a list of values. You will receive an array of values, each passed through the `type` function (if specified). * * ```js * const optionDefinitions = [ * { name: 'files', type: String, multiple: true } * ] * ``` * * Note, examples 1 and 3 below demonstrate "greedy" parsing which can be disabled by using `lazyMultiple`. * * | # | argv input | commandLineArgs() output | * | --- | ------------ | ------------ | * | 1 | `--files one.js two.js` | `{ files: [ 'one.js', 'two.js' ] }` | * | 2 | `--files one.js --files two.js` | `{ files: [ 'one.js', 'two.js' ] }` | * | 3 | `--files *` | `{ files: [ 'one.js', 'two.js' ] }` | * * @type {boolean} */ this.multiple = definition.multiple; /** * Identical to `multiple` but with greedy parsing disabled. * * ```js * const optionDefinitions = [ * { name: 'files', lazyMultiple: true }, * { name: 'verbose', alias: 'v', type: Boolean, lazyMultiple: true } * ] * ``` * * | # | argv input | commandLineArgs() output | * | --- | ------------ | ------------ | * | 1 | `--files one.js --files two.js` | `{ files: [ 'one.js', 'two.js' ] }` | * | 2 | `-vvv` | `{ verbose: [ true, true, true ] }` | * * @type {boolean} */ this.lazyMultiple = definition.lazyMultiple; /** * Any values unaccounted for by an option definition will be set on the `defaultOption`. This flag is typically set on the most commonly-used option to make for more concise usage (i.e. `$ example *.js` instead of `$ example --files *.js`). * * ```js * const optionDefinitions = [ * { name: 'files', multiple: true, defaultOption: true } * ] * ``` * * | # | argv input | commandLineArgs() output | * | --- | ------------ | ------------ | * | 1 | `--files one.js two.js` | `{ files: [ 'one.js', 'two.js' ] }` | * | 2 | `one.js two.js` | `{ files: [ 'one.js', 'two.js' ] }` | * | 3 | `*` | `{ files: [ 'one.js', 'two.js' ] }` | * * @type {boolean} */ this.defaultOption = definition.defaultOption; /** * An initial value for the option. * * ```js * const optionDefinitions = [ * { name: 'files', multiple: true, defaultValue: [ 'one.js' ] }, * { name: 'max', type: Number, defaultValue: 3 } * ] * ``` * * | # | argv input | commandLineArgs() output | * | --- | ------------ | ------------ | * | 1 | | `{ files: [ 'one.js' ], max: 3 }` | * | 2 | `--files two.js` | `{ files: [ 'two.js' ], max: 3 }` | * | 3 | `--max 4` | `{ files: [ 'one.js' ], max: 4 }` | * * @type {*} */ this.defaultValue = definition.defaultValue; /** * When your app has a large amount of options it makes sense to organise them in groups. * * There are two automatic groups: `_all` (contains all options) and `_none` (contains options without a `group` specified in their definition). * * ```js * const optionDefinitions = [ * { name: 'verbose', group: 'standard' }, * { name: 'help', group: [ 'standard', 'main' ] }, * { name: 'compress', group: [ 'server', 'main' ] }, * { name: 'static', group: 'server' }, * { name: 'debug' } * ] * ``` * *<table> * <tr> * <th>#</th><th>Command Line</th><th>commandLineArgs() output</th> * </tr> * <tr> * <td>1</td><td><code>--verbose</code></td><td><pre><code> *{ * _all: { verbose: true }, * standard: { verbose: true } *} *</code></pre></td> * </tr> * <tr> * <td>2</td><td><code>--debug</code></td><td><pre><code> *{ * _all: { debug: true }, * _none: { debug: true } *} *</code></pre></td> * </tr> * <tr> * <td>3</td><td><code>--verbose --debug --compress</code></td><td><pre><code> *{ * _all: { * verbose: true, * debug: true, * compress: true * }, * standard: { verbose: true }, * server: { compress: true }, * main: { compress: true }, * _none: { debug: true } *} *</code></pre></td> * </tr> * <tr> * <td>4</td><td><code>--compress</code></td><td><pre><code> *{ * _all: { compress: true }, * server: { compress: true }, * main: { compress: true } *} *</code></pre></td> * </tr> *</table> * * @type {string|string[]} */ this.group = definition.group; /* pick up any remaining properties */ for (const prop in definition) { if (!this[prop]) this[prop] = definition[prop]; } } isBoolean () { return this.type === Boolean || (t.isFunction(this.type) && this.type.name === 'Boolean') } isMultiple () { return this.multiple || this.lazyMultiple } static create (def) { const result = new this(def); return result } } /** * @module option-definitions */ /** * @alias module:option-definitions */ class Definitions extends Array { /** * validate option definitions * @param {boolean} [caseInsensitive=false] - whether arguments will be parsed in a case insensitive manner * @returns {string} */ validate (caseInsensitive) { const someHaveNoName = this.some(def => !def.name); if (someHaveNoName) { halt( 'INVALID_DEFINITIONS', 'Invalid option definitions: the `name` property is required on each definition' ); } const someDontHaveFunctionType = this.some(def => def.type && typeof def.type !== 'function'); if (someDontHaveFunctionType) { halt( 'INVALID_DEFINITIONS', 'Invalid option definitions: the `type` property must be a setter fuction (default: `Boolean`)' ); } let invalidOption; const numericAlias = this.some(def => { invalidOption = def; return t.isDefined(def.alias) && t.isNumber(def.alias) }); if (numericAlias) { halt( 'INVALID_DEFINITIONS', 'Invalid option definition: to avoid ambiguity an alias cannot be numeric [--' + invalidOption.name + ' alias is -' + invalidOption.alias + ']' ); } const multiCharacterAlias = this.some(def => { invalidOption = def; return t.isDefined(def.alias) && def.alias.length !== 1 }); if (multiCharacterAlias) { halt( 'INVALID_DEFINITIONS', 'Invalid option definition: an alias must be a single character' ); } const hypenAlias = this.some(def => { invalidOption = def; return def.alias === '-' }); if (hypenAlias) { halt( 'INVALID_DEFINITIONS', 'Invalid option definition: an alias cannot be "-"' ); } const duplicateName = hasDuplicates(this.map(def => caseInsensitive ? def.name.toLowerCase() : def.name)); if (duplicateName) { halt( 'INVALID_DEFINITIONS', 'Two or more option definitions have the same name' ); } const duplicateAlias = hasDuplicates(this.map(def => caseInsensitive && t.isDefined(def.alias) ? def.alias.toLowerCase() : def.alias)); if (duplicateAlias) { halt( 'INVALID_DEFINITIONS', 'Two or more option definitions have the same alias' ); } const duplicateDefaultOption = this.filter(def => def.defaultOption === true).length > 1; if (duplicateDefaultOption) { halt( 'INVALID_DEFINITIONS', 'Only one option definition can be the defaultOption' ); } const defaultBoolean = this.some(def => { invalidOption = def; return def.isBoolean() && def.defaultOption }); if (defaultBoolean) { halt( 'INVALID_DEFINITIONS', `A boolean option ["${invalidOption.name}"] can not also be the defaultOption.` ); } } /** * Get definition by option arg (e.g. `--one` or `-o`) * @param {string} [arg] the argument name to get the definition for * @param {boolean} [caseInsensitive] whether to use case insensitive comparisons when finding the appropriate definition * @returns {Definition} */ get (arg, caseInsensitive) { if (isOption(arg)) { if (re$1.short.test(arg)) { const shortOptionName = getOptionName(arg); if (caseInsensitive) { const lowercaseShortOptionName = shortOptionName.toLowerCase(); return this.find(def => t.isDefined(def.alias) && def.alias.toLowerCase() === lowercaseShortOptionName) } else { return this.find(def => def.alias === shortOptionName) } } else { const optionName = getOptionName(arg); if (caseInsensitive) { const lowercaseOptionName = optionName.toLowerCase(); return this.find(def => def.name.toLowerCase() === lowercaseOptionName) } else { return this.find(def => def.name === optionName) } } } else { return this.find(def => def.name === arg) } } getDefault () { return this.find(def => def.defaultOption === true) } isGrouped () { return this.some(def => def.group) } whereGrouped () { return this.filter(containsValidGroup) } whereNotGrouped () { return this.filter(def => !containsValidGroup(def)) } whereDefaultValueSet () { return this.filter(def => t.isDefined(def.defaultValue)) } static from (definitions, caseInsensitive) { if (definitions instanceof this) return definitions const result = super.from(arrayify(definitions), def => OptionDefinition.create(def)); result.validate(caseInsensitive); return result } } function halt (name, message) { const err = new Error(message); err.name = name; throw err } function containsValidGroup (def) { return arrayify(def.group).some(group => group) } function hasDuplicates (array) { const items = {}; for (let i = 0; i < array.length; i++) { const value = array[i]; if (items[value]) { return true } else { if (t.isDefined(value)) items[value] = true; } } } /** * @module argv-parser */ /** * @alias module:argv-parser */ class ArgvParser { /** * @param {OptionDefinitions} - Definitions array * @param {object} [options] - Options * @param {string[]} [options.argv] - Overrides `process.argv` * @param {boolean} [options.stopAtFirstUnknown] - * @param {boolean} [options.caseInsensitive] - Arguments will be parsed in a case insensitive manner. Defaults to false. */ constructor (definitions, options) { this.options = Object.assign({}, options); /** * Option Definitions */ this.definitions = Definitions.from(definitions, this.options.caseInsensitive); /** * Argv */ this.argv = ArgvArray.from(this.options.argv); if (this.argv.hasCombinedShortOptions()) { findReplace(this.argv, re$1.combinedShort.test.bind(re$1.combinedShort), arg => { arg = arg.slice(1); return arg.split('').map(letter => ({ origArg: `-${arg}`, arg: '-' + letter })) }); } } /** * Yields one `{ event, name, value, arg, def }` argInfo object for each arg in `process.argv` (or `options.argv`). */ * [Symbol.iterator] () { const definitions = this.definitions; let def; let value; let name; let event; let singularDefaultSet = false; let unknownFound = false; let origArg; for (let arg of this.argv) { if (t.isPlainObject(arg)) { origArg = arg.origArg; arg = arg.arg; } if (unknownFound && this.options.stopAtFirstUnknown) {