Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cURL's boolean options #270

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
67 changes: 67 additions & 0 deletions curl_opts.js
Original file line number Diff line number Diff line change
@@ -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(/^( -(?<short>.),)? +--(?<long>\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 <name>
// or
// --proxy [protocol://]host[:port]
// it's not a boolean option.
const isBooleanOpt = !(line.match(/^( -(?<short>.),)? +--(?<long>\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 })
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
186 changes: 175 additions & 11 deletions util.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const cookie = require('cookie')
const yargs = require('yargs')
const yargsParser = require('yargs-parser')
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides have a different API that chains method calls instead of passing an object, I don't understand what yargs actually does for us over plain yargs-parser, so I removed it.

const URL = require('url')
const querystring = require('query-string')
const nunjucks = require('nunjucks')
Expand Down Expand Up @@ -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
Expand Down