diff --git a/README.md b/README.md index 33ca42e8..814839bb 100644 --- a/README.md +++ b/README.md @@ -468,10 +468,19 @@ npx: installed 1 in 0.63s LZfXLFzPPR4NNrgjlWDxn ``` -If you want to change alphabet or ID size, you should use [`nanoid-cli`]. +Size of generated ID can be specified with `--size` (or `-s`) option: -[`nanoid-cli`]: https://github.com/twhitbeck/nanoid-cli +```sh +$ npx nanoid --size 10 +L3til0JS4z +``` + +Custom alphabet can be specified with `--alphabet` (or `-a`) option (note that in this case `--size` is required): +```sh +$ npx nanoid --alphabet abc --size 15 +bccbcabaabaccab +``` ### Other Programming Languages diff --git a/README.ru.md b/README.ru.md index bc7f9800..75e1ecf4 100644 --- a/README.ru.md +++ b/README.ru.md @@ -456,10 +456,19 @@ npx: installed 1 in 0.63s LZfXLFzPPR4NNrgjlWDxn ``` -Для смены алфавита или длины ID есть отдельный проект [`nanoid-cli`]. +Длину генерируемых ID можно передать в аргументе `--size` (или `-s`): -[`nanoid-cli`]: https://github.com/twhitbeck/nanoid-cli +```sh +$ npx nanoid --size 10 +L3til0JS4z +``` + +Изменить алфавит можно при помощи аргумента `--alphabet` (ли `-a`) (обратите внимание, что в этом случае `--size` обязателен): +```sh +$ npx nanoid --alphabet abc --size 15 +bccbcabaabaccab +``` ### Другие языки программирования diff --git a/bin/nanoid.cjs b/bin/nanoid.cjs index e14116f6..16ab4934 100755 --- a/bin/nanoid.cjs +++ b/bin/nanoid.cjs @@ -1,5 +1,46 @@ #!/usr/bin/env node -let { nanoid } = require('..') +let { nanoid, customAlphabet } = require('..') +let { parseArgs } = require('./utils') -process.stdout.write(nanoid() + '\n') +let parsedArgs = parseArgs(process.argv) + +if (parsedArgs.help) { + process.stdout.write(` + Usage + $ nanoid [options] + + Options + -s, --size Generated ID size + -a, --alphabet Alphabet to use + -h, --help Show this help + + Examples + $ nano --s=15 + S9sBF77U6sDB8Yg + + $ nano --size=10 --alphabet=abc + bcabababca +`) + process.exit() +} + +let alphabet = parsedArgs.alphabet || parsedArgs.a +let size = parsedArgs.size || parsedArgs.s ? Number(parsedArgs.size || parsedArgs.s) : undefined + +if (typeof size !== 'undefined' && (Number.isNaN(size) || size <= 0)) { + process.stderr.write('Size must be positive integer\n') + process.exit(1) +} + +if (alphabet) { + if (typeof size === 'undefined') { + process.stderr.write('You must also specify size option, when using custom alphabet\n') + process.exit(1) + } + process.stdout.write(customAlphabet(alphabet, size)()) +} else { + process.stdout.write(nanoid(size)) +} + +process.stdout.write('\n') diff --git a/bin/nanoid.test.js b/bin/nanoid.test.js index 43753706..005af89b 100644 --- a/bin/nanoid.test.js +++ b/bin/nanoid.test.js @@ -1,15 +1,74 @@ -let { test } = require('uvu') -let { is, match } = require('uvu/assert') +let { suite } = require('uvu') +let { is, match, equal } = require('uvu/assert') let { promisify } = require('util') let { join } = require('path') let child = require('child_process') +let { parseArgs } = require('./utils') + let exec = promisify(child.exec) -test('prints unique ID', async () => { +const nanoIdSuit = suite('nanoid') + +nanoIdSuit('prints unique ID', async () => { let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs')) is(stderr, '') match(stdout, /^[\w-]{21}\n$/) }) -test.run() +nanoIdSuit('uses size', async () => { + let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --size=10') + is(stderr, '') + match(stdout, /^[\w-]{10}\n$/) +}) + +nanoIdSuit('uses alphabet', async () => { + let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --alphabet=abc --size=15') + is(stderr, '') + match(stdout, /^[abc]{15}\n$/) +}) + +nanoIdSuit('show an error if size is not a number', async () => { + try { + await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --s abc') + } catch (e) { + match(e, /Size must be positive integer/) + } +}) + +nanoIdSuit('shows an error if size is not provided when using custom alphabet', async () => { + try { + await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --alphabet abc') + } catch (e) { + match(e, /You must also specify size option, when using custom alphabet/) + } +}) + +nanoIdSuit('requires error if size is a negative number', async () => { + try { + await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --size "-1"') + } catch (e) { + match(e, /Size must be positive integer/) + } +}) + +nanoIdSuit('displays help', async () => { + let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --help') + is(stderr, '') + match(stdout, /Usage/) + match(stdout, /\$ nanoid \[options]/) +}) + +nanoIdSuit.run() + +const parseArgsSuite = suite('parseArgs') + +parseArgsSuite('parses args', () => { + equal(parseArgs(['node', 'nanoid.cjs', '--help']), { help: true }) + equal(parseArgs(['node', 'nanoid.cjs', '--help', '--size=30']), { help: true, size: '30' }) + equal(parseArgs(['node', 'nanoid.cjs', '--help', '-s', '30']), { help: true, s: '30' }) + equal(parseArgs(['node', 'nanoid.cjs', '--help', '-size', '30']), { help: true, size: '30' }) + equal(parseArgs(['node', 'nanoid.cjs', '--help', '-size', '30', '-alphabet', 'abc']), { help: true, size: '30', alphabet: 'abc' }) +}) + +parseArgsSuite.run() diff --git a/bin/utils/index.js b/bin/utils/index.js new file mode 100644 index 00000000..914cdad2 --- /dev/null +++ b/bin/utils/index.js @@ -0,0 +1,44 @@ +let cleanArgName = (arg) => arg.startsWith('--') ? arg.slice(2) : arg.slice(1) + +let parseArgs = (argv) => { + argv.splice(0, 2) + + let parsedArgs = {} + + let currentArg = null + argv.forEach((arg) => { + if (arg.includes('=')) { + if (currentArg) { + parsedArgs[currentArg] = true + currentArg = null + } + let argSplit = arg.split('=') + parsedArgs[cleanArgName(argSplit[0])] = argSplit[1] + + return + } + + if (arg.startsWith('-') || arg.startsWith('--')) { + if (currentArg) { + parsedArgs[currentArg] = true + currentArg = null + } + + currentArg = cleanArgName(arg) + return + } + + if (currentArg) { + parsedArgs[currentArg] = arg + currentArg = null + } + }) + + if (currentArg) { + parsedArgs[currentArg] = true + } + + return parsedArgs +} + +module.exports = { parseArgs }