Created
August 11, 2020 21:06
-
-
Save tjklemz/21f245637115a9f17690e5fe6b75c585 to your computer and use it in GitHub Desktop.
Partout: Simple command line parser for Node
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
partout([ | |
'artifact', | |
{ name: 'output', defaultValue: '.' }, | |
{ name: 'platform', flag: true, parse: true } | |
], '-p darwin app'.split(' ')) | |
*/ | |
const identity = (value) => value; | |
const getFlag = (option) => { | |
const { name, parse, flag, toggle, defaultValue } = option; | |
const short = typeof flag === 'string' ? flag : name.charAt(0); | |
return { name, short, parse: parse && typeof parse === 'boolean' ? identity : (parse || identity), toggle, defaultValue }; | |
}; | |
const getPositional = (positional) => { | |
const name = typeof positional === 'string' ? positional : positional.name; | |
const parse = typeof positional === 'string' ? identity : positional.parse; | |
const defaultValue = typeof positional === 'string' ? undefined : positional.defaultValue; | |
return { name, parse: parse && typeof parse === 'boolean' ? identity : (parse || identity), defaultValue }; | |
}; | |
const parseArgs = async (options = [], args = process.argv.slice(2)) => { | |
const flags = options.filter(option => typeof option === 'object' && option.flag); | |
const positional = options.filter(option => typeof option === 'string' || (typeof option === 'object' && !option.flag)); | |
const parsed = {}; | |
let j = 0; | |
for (let i = 0; i < args.length; ++i) { | |
const flag = flags.find((flag) => { | |
const { name, short } = getFlag(flag); | |
return `-${short}` === args[i] || `--${name}` === args[i]; | |
}); | |
if (flag) { | |
const { name, parse, toggle } = getFlag(flag); | |
if (!toggle) { | |
i += 1; | |
parsed[name] = await parse(args[i]); | |
} else { | |
parsed[name] = true; | |
} | |
} else if (positional[j]) { | |
const { name, parse } = getPositional(positional[j]); | |
parsed[name] = await parse(args[i]); | |
j += 1; | |
} | |
} | |
for (const flag of flags) { | |
const { name, parse, toggle, defaultValue } = getFlag(flag); | |
if (!(name in parsed)) { | |
parsed[name] = await parse(toggle && typeof defaultValue === 'undefined' ? false : defaultValue); | |
} | |
} | |
for (; j < positional.length; ++j) { | |
const { name, parse, defaultValue } = getPositional(positional[j]); | |
parsed[name] = await parse(defaultValue); | |
} | |
return parsed; | |
}; | |
module.exports = parseArgs; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const test = require('ava'); | |
const parseArgs = require('./partout'); | |
test('basic options', async (t) => { | |
const args = '-p darwin app'.split(' '); | |
const result = await parseArgs([ | |
'artifact', | |
{ name: 'output', defaultValue: '.' }, | |
{ name: 'platform', flag: true, parse: true } | |
], args); | |
t.like(result, { | |
artifact: 'app', | |
output: '.', | |
platform: 'darwin', | |
}); | |
}); | |
const parsePlatform = platform => { | |
if (!['darwin', 'win32'].includes(platform)) { | |
throw new Error(`Platform must be "darwin" or "win32". Got: ${platform}`) | |
} | |
return platform; | |
}; | |
const parseOutput = async output => { | |
await new Promise(resolve => setTimeout(resolve, 5)); | |
if (output !== '.' && !output.endsWith('/out')) { | |
throw new Error(`Output is not valid. Got: ${output}`); | |
} | |
return output.split('/'); | |
}; | |
test('parsing success', async (t) => { | |
const args = '-p darwin app ./my/out'.split(' '); | |
const result = await parseArgs([ | |
'artifact', | |
{ name: 'output', defaultValue: '.', parse: parseOutput }, | |
{ name: 'platform', flag: true, parse: parsePlatform }, | |
], args); | |
t.like(result, { | |
artifact: 'app', | |
output: ['.', 'my', 'out'], | |
platform: 'darwin', | |
}); | |
}); | |
test('parsing fail', async (t) => { | |
const args = '-p mac app ./my/out'.split(' '); | |
await t.throwsAsync(async () => { | |
await parseArgs([ | |
'artifact', | |
{ name: 'output', defaultValue: '.', parse: parseOutput }, | |
{ name: 'platform', flag: true, parse: parsePlatform }, | |
], args); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment