Skip to content

Commit 32b9bda

Browse files
authored
Allows passing size or custom alphabet via cli as args (#334)
* Allows passing size or custom alphabet via cli as args * Increase test coverage, make work with Node 12
1 parent 246d5f8 commit 32b9bda

File tree

5 files changed

+172
-10
lines changed

5 files changed

+172
-10
lines changed

README.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -468,10 +468,19 @@ npx: installed 1 in 0.63s
468468
LZfXLFzPPR4NNrgjlWDxn
469469
```
470470

471-
If you want to change alphabet or ID size, you should use [`nanoid-cli`].
471+
Size of generated ID can be specified with `--size` (or `-s`) option:
472472

473-
[`nanoid-cli`]: https://github.com/twhitbeck/nanoid-cli
473+
```sh
474+
$ npx nanoid --size 10
475+
L3til0JS4z
476+
```
477+
478+
Custom alphabet can be specified with `--alphabet` (or `-a`) option (note that in this case `--size` is required):
474479

480+
```sh
481+
$ npx nanoid --alphabet abc --size 15
482+
bccbcabaabaccab
483+
```
475484

476485
### Other Programming Languages
477486

README.ru.md

+11-2
Original file line numberDiff line numberDiff line change
@@ -456,10 +456,19 @@ npx: installed 1 in 0.63s
456456
LZfXLFzPPR4NNrgjlWDxn
457457
```
458458

459-
Для смены алфавита или длины ID есть отдельный проект [`nanoid-cli`].
459+
Длину генерируемых ID можно передать в аргументе `--size` (или `-s`):
460460

461-
[`nanoid-cli`]: https://github.com/twhitbeck/nanoid-cli
461+
```sh
462+
$ npx nanoid --size 10
463+
L3til0JS4z
464+
```
465+
466+
Изменить алфавит можно при помощи аргумента `--alphabet` (ли `-a`) (обратите внимание, что в этом случае `--size` обязателен):
462467

468+
```sh
469+
$ npx nanoid --alphabet abc --size 15
470+
bccbcabaabaccab
471+
```
463472

464473
### Другие языки программирования
465474

bin/nanoid.cjs

+43-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,46 @@
11
#!/usr/bin/env node
22

3-
let { nanoid } = require('..')
3+
let { nanoid, customAlphabet } = require('..')
4+
let { parseArgs } = require('./utils')
45

5-
process.stdout.write(nanoid() + '\n')
6+
let parsedArgs = parseArgs(process.argv)
7+
8+
if (parsedArgs.help) {
9+
process.stdout.write(`
10+
Usage
11+
$ nanoid [options]
12+
13+
Options
14+
-s, --size Generated ID size
15+
-a, --alphabet Alphabet to use
16+
-h, --help Show this help
17+
18+
Examples
19+
$ nano --s=15
20+
S9sBF77U6sDB8Yg
21+
22+
$ nano --size=10 --alphabet=abc
23+
bcabababca
24+
`)
25+
process.exit()
26+
}
27+
28+
let alphabet = parsedArgs.alphabet || parsedArgs.a
29+
let size = parsedArgs.size || parsedArgs.s ? Number(parsedArgs.size || parsedArgs.s) : undefined
30+
31+
if (typeof size !== 'undefined' && (Number.isNaN(size) || size <= 0)) {
32+
process.stderr.write('Size must be positive integer\n')
33+
process.exit(1)
34+
}
35+
36+
if (alphabet) {
37+
if (typeof size === 'undefined') {
38+
process.stderr.write('You must also specify size option, when using custom alphabet\n')
39+
process.exit(1)
40+
}
41+
process.stdout.write(customAlphabet(alphabet, size)())
42+
} else {
43+
process.stdout.write(nanoid(size))
44+
}
45+
46+
process.stdout.write('\n')

bin/nanoid.test.js

+63-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,74 @@
1-
let { test } = require('uvu')
2-
let { is, match } = require('uvu/assert')
1+
let { suite } = require('uvu')
2+
let { is, match, equal } = require('uvu/assert')
33
let { promisify } = require('util')
44
let { join } = require('path')
55
let child = require('child_process')
66

7+
let { parseArgs } = require('./utils')
8+
79
let exec = promisify(child.exec)
810

9-
test('prints unique ID', async () => {
11+
const nanoIdSuit = suite('nanoid')
12+
13+
nanoIdSuit('prints unique ID', async () => {
1014
let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs'))
1115
is(stderr, '')
1216
match(stdout, /^[\w-]{21}\n$/)
1317
})
1418

15-
test.run()
19+
nanoIdSuit('uses size', async () => {
20+
let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --size=10')
21+
is(stderr, '')
22+
match(stdout, /^[\w-]{10}\n$/)
23+
})
24+
25+
nanoIdSuit('uses alphabet', async () => {
26+
let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --alphabet=abc --size=15')
27+
is(stderr, '')
28+
match(stdout, /^[abc]{15}\n$/)
29+
})
30+
31+
nanoIdSuit('show an error if size is not a number', async () => {
32+
try {
33+
await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --s abc')
34+
} catch (e) {
35+
match(e, /Size must be positive integer/)
36+
}
37+
})
38+
39+
nanoIdSuit('shows an error if size is not provided when using custom alphabet', async () => {
40+
try {
41+
await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --alphabet abc')
42+
} catch (e) {
43+
match(e, /You must also specify size option, when using custom alphabet/)
44+
}
45+
})
46+
47+
nanoIdSuit('requires error if size is a negative number', async () => {
48+
try {
49+
await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --size "-1"')
50+
} catch (e) {
51+
match(e, /Size must be positive integer/)
52+
}
53+
})
54+
55+
nanoIdSuit('displays help', async () => {
56+
let { stdout, stderr } = await exec('node ' + join(__dirname, 'nanoid.cjs') + ' --help')
57+
is(stderr, '')
58+
match(stdout, /Usage/)
59+
match(stdout, /\$ nanoid \[options]/)
60+
})
61+
62+
nanoIdSuit.run()
63+
64+
const parseArgsSuite = suite('parseArgs')
65+
66+
parseArgsSuite('parses args', () => {
67+
equal(parseArgs(['node', 'nanoid.cjs', '--help']), { help: true })
68+
equal(parseArgs(['node', 'nanoid.cjs', '--help', '--size=30']), { help: true, size: '30' })
69+
equal(parseArgs(['node', 'nanoid.cjs', '--help', '-s', '30']), { help: true, s: '30' })
70+
equal(parseArgs(['node', 'nanoid.cjs', '--help', '-size', '30']), { help: true, size: '30' })
71+
equal(parseArgs(['node', 'nanoid.cjs', '--help', '-size', '30', '-alphabet', 'abc']), { help: true, size: '30', alphabet: 'abc' })
72+
})
73+
74+
parseArgsSuite.run()

bin/utils/index.js

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
let cleanArgName = (arg) => arg.startsWith('--') ? arg.slice(2) : arg.slice(1)
2+
3+
let parseArgs = (argv) => {
4+
argv.splice(0, 2)
5+
6+
let parsedArgs = {}
7+
8+
let currentArg = null
9+
argv.forEach((arg) => {
10+
if (arg.includes('=')) {
11+
if (currentArg) {
12+
parsedArgs[currentArg] = true
13+
currentArg = null
14+
}
15+
let argSplit = arg.split('=')
16+
parsedArgs[cleanArgName(argSplit[0])] = argSplit[1]
17+
18+
return
19+
}
20+
21+
if (arg.startsWith('-') || arg.startsWith('--')) {
22+
if (currentArg) {
23+
parsedArgs[currentArg] = true
24+
currentArg = null
25+
}
26+
27+
currentArg = cleanArgName(arg)
28+
return
29+
}
30+
31+
if (currentArg) {
32+
parsedArgs[currentArg] = arg
33+
currentArg = null
34+
}
35+
})
36+
37+
if (currentArg) {
38+
parsedArgs[currentArg] = true
39+
}
40+
41+
return parsedArgs
42+
}
43+
44+
module.exports = { parseArgs }

0 commit comments

Comments
 (0)