diff --git a/curl_opts.js b/curl_opts.js new file mode 100644 index 000000000..d2ccb8d2c --- /dev/null +++ b/curl_opts.js @@ -0,0 +1,67 @@ +// This script generates a list of all of cURL's options +// by parsing `curl --help` + +const { execSync } = require('child_process') + +const stdin = execSync('curl --help').toString() + +// https://github.com/curl/curl/pull/7378 +const actuallyNotBooleanOpts = ['tlspassword', 'stderr', 'request-target', 'quote'] + +const booleanOpts = [] +// Use Map to maintain the same order as `curl --help` to reduce churn in diffs +// TODO: format Map as object instead of having to do it by hand +const aliases = new Map() +for (const line of stdin.split('\n')) { + if (line.startsWith('Usage: ') || !line) { + continue + } + const match = line.match(/^( -(?.),)? +--(?\S+) /) + if (!match) { + console.error('failed to match line:', line) + continue + } + const short = match.groups.short + let long = match.groups.long + + // All of cURL's short options have a long form + if (short && !long) { + console.error('short option doesn\'t have long option', line) + } + + // If line looks something like + // --user-agent + // or + // --proxy [protocol://]host[:port] + // it's not a boolean option. + const isBooleanOpt = !(line.match(/^( -(?.),)? +--(?\S+) +[[<]/)) && !actuallyNotBooleanOpts.includes(long) + // Should be checked by hand + // console.error((isBooleanOpt + '').padEnd(5), line) + + if (long.startsWith('no-')) { + if (!isBooleanOpt) { + console.error('non-boolean option starts with negation prefix "no-"', line) + } + // Yargs' 'boolean-negation' (enabled by default) takes care of these. + // + // There's a -N short option which is short for --no-buffer, but because we remove the + // "no-" here, -N is incorrectly aliased to --buffer instead. + // This is okay because we don't do anything with --buffer's value, we just need yargs to + // parse it as a boolean option. + // https://github.com/yargs/yargs-parser/issues/406 + long = long.slice(3) + } + + if (short && long) { + aliases.set(short, long) + } + if (isBooleanOpt) { + booleanOpts.push(long) + } +} + +console.dir({ + boolean: booleanOpts, + alias: aliases, + configuration: {} +}, { maxArrayLength: null }) diff --git a/package.json b/package.json index 7b5a9010f..3e343253a 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,8 @@ "query-string": "^6.13.1", "string.prototype.startswith": "^0.2.0", "yamljs": "^0.3.0", - "yargs": "git@github.com:NickCarneiro/yargs-ansi.git" + "yargs": "git@github.com:NickCarneiro/yargs-ansi.git", + "yargs-parser": "git@github.com:NickCarneiro/yargs-parser-ansi.git" }, "devDependencies": { "standard": "^14.3.1", diff --git a/util.js b/util.js index 03a9435ff..7ccf77ee9 100644 --- a/util.js +++ b/util.js @@ -1,5 +1,5 @@ const cookie = require('cookie') -const yargs = require('yargs') +const yargsParser = require('yargs-parser') const URL = require('url') const querystring = require('query-string') const nunjucks = require('nunjucks') @@ -28,16 +28,180 @@ const parseCurlCommand = curlCommand => { curlCommand = curlCommand.replace(/ -Xnull/, ' ') curlCommand = curlCommand.trim() - // Parse with some understanding of the meanings of flags. In particular, - // boolean flags can be trouble if the URL to fetch follows immediately - // after, since it will be taken as an argument to the flag rather than - // interpreted as a positional argument. Someone should add all the flags - // likely to cause trouble here. - const parsedArguments = yargs - .boolean(['I', 'head', 'compressed', 'L', 'k', 'silent', 's']) - .alias('H', 'header') - .alias('A', 'user-agent') - .parse(curlCommand) + // Parse with some understanding of the meanings of flags. + const parsedArguments = yargsParser( + curlCommand, + // This is generated by curl_opts.js + { + // boolean flags can be trouble if the URL to fetch follows immediately + // after, since yargs will interpret the URL as an argument to the flag + // rather than interpreting it as a positional argument. + boolean: [ + 'anyauth', + 'append', + 'basic', + 'cert-status', + 'compressed', + 'compressed-ssh', + 'create-dirs', + 'crlf', + 'digest', + 'disable', + 'disable-eprt', + 'disable-epsv', + 'disallow-username-in-url', + 'fail', + 'fail-early', + 'false-start', + 'ftp-create-dirs', + 'ftp-pasv', + 'ftp-pret', + 'ftp-skip-pasv-ip', + 'ftp-ssl-ccc', + 'ftp-ssl-control', + 'get', + 'globoff', + 'haproxy-protocol', + 'head', + 'help', + 'http0.9', + 'http1.0', + 'http1.1', + 'http2', + 'http2-prior-knowledge', + 'ignore-content-length', + 'include', + 'insecure', + 'ipv4', + 'ipv6', + 'junk-session-cookies', + 'list-only', + 'location', + 'location-trusted', + 'manual', + 'metalink', + 'negotiate', + 'netrc', + 'netrc-optional', + 'next', + 'alpn', + 'buffer', + 'keepalive', + 'npn', + 'sessionid', + 'ntlm', + 'ntlm-wb', + 'path-as-is', + 'post301', + 'post302', + 'post303', + 'progress-bar', + 'proxy-anyauth', + 'proxy-basic', + 'proxy-digest', + 'proxy-insecure', + 'proxy-negotiate', + 'proxy-ntlm', + 'proxy-ssl-allow-beast', + 'proxy-tlsv1', + 'proxytunnel', + 'raw', + 'remote-header-name', + 'remote-name', + 'remote-name-all', + 'remote-time', + 'retry-connrefused', + 'sasl-ir', + 'show-error', + 'silent', + 'socks5-basic', + 'socks5-gssapi', + 'socks5-gssapi-nec', + 'ssl', + 'ssl-allow-beast', + 'ssl-no-revoke', + 'ssl-reqd', + 'sslv2', + 'sslv3', + 'styled-output', + 'suppress-connect-headers', + 'tcp-fastopen', + 'tcp-nodelay', + 'tftp-no-options', + 'tlsv1', + 'tlsv1.0', + 'tlsv1.1', + 'tlsv1.2', + 'tlsv1.3', + 'tr-encoding', + 'trace-time', + 'use-ascii', + 'verbose', + 'version', + 'xattr' + ], + alias: { + a: 'append', + E: 'cert', + K: 'config', + C: 'continue-at', + b: 'cookie', + c: 'cookie-jar', + d: 'data', + q: 'disable', + D: 'dump-header', + f: 'fail', + F: 'form', + P: 'ftp-port', + G: 'get', + g: 'globoff', + I: 'head', + H: 'header', + h: 'help', + 0: 'http1.0', + i: 'include', + k: 'insecure', + 4: 'ipv4', + 6: 'ipv6', + j: 'junk-session-cookies', + l: 'list-only', + L: 'location', + M: 'manual', + m: 'max-time', + n: 'netrc', + ':': 'next', + N: 'buffer', + o: 'output', + '#': 'progress-bar', + x: 'proxy', + U: 'proxy-user', + p: 'proxytunnel', + Q: 'quote', + r: 'range', + e: 'referer', + J: 'remote-header-name', + O: 'remote-name', + R: 'remote-time', + X: 'request', + S: 'show-error', + s: 'silent', + Y: 'speed-limit', + y: 'speed-time', + 2: 'sslv2', + 3: 'sslv3', + t: 'telnet-option', + z: 'time-cond', + 1: 'tlsv1', + T: 'upload-file', + B: 'use-ascii', + u: 'user', + A: 'user-agent', + v: 'verbose', + V: 'version', + w: 'write-out' + }, + } + ) let cookieString let cookies