diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5dd87bd --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +package-lock.json +yarn.lock +.nyc_output/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..43c97e7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +package-lock=false diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..0c1dd10 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,14 @@ +language: node_js + +node_js: + - "8" + - "10" + - "12" + - "13" + +script: + - npm run test-cov + +after_script: + - npm install coveralls + - nyc report --reporter=text-lcov | coveralls diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..17304de --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 dailyrandomphoto + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..0558234 --- /dev/null +++ b/README.md @@ -0,0 +1,105 @@ +# id-generators + +[![NPM Version][npm-version-image]][npm-url] +[![LICENSE][license-image]][license-url] +[![Build Status][travis-image]][travis-url] +[![Coverage Status][coveralls-image]][coveralls-url] +[![dependencies Status][dependencies-image]][dependencies-url] +[![devDependencies Status][devDependencies-image]][devDependencies-url] + +**id-generators** is small JavaScript library to generate ID with awesome Unique ID libraries. + +## Installation + +```sh +npm install id-generators +``` + +## Usages + +```js +const generators = require('id-generators'); +const generator = generators.get('nanoid'); +const generate = generator(); + +console.log('ID: ' + generate()); +// ID: gCa0wL_8ElTje5cwOci1d +console.log('ID: ' + generate()); +// ID: 5C_YAjgQl4iM3zK-TValY +console.log('ID: ' + generate()); +// ID: gQOOwCoQyfCuGK7fgINLd +``` + +If you want to customize ID, you can pass an option as an argument to the `generator` function. +```js +const generators = require('id-generators'); +const generator = generators.get('nanoid-simple'); +const generate = generator({size: 25}); + +console.log('ID: ' + generate()); +// ID: oa9wj0kfm50gv2qse8l5xup0u +console.log('ID: ' + generate()); +// ID: xn5ff5odiylg4jehhhp9vtlxv +console.log('ID: ' + generate()); +// ID: dff7ay5x38k1pkc2pxjv7elky +``` + +generator type | ID length | character set |options/default | description +--- | --- | --- | --- | --- +cuid (default) | 25 | `a-z0-9`, start with `c` | | use [cuid()](https://github.com/ericelliott/cuid) generated string.
e.g. `ck2bi7fxf00013ryng5jr1rer` +cuid-slug | 7-10 | `a-z0-9` | | use [cuid.slug()](https://github.com/ericelliott/cuid) generated string.
e.g. `xh23npi` +nanoid | 21 | `A-Za-z0-9_-` | size/21 | use [nanoid()](https://github.com/ai/nanoid) generated string.
e.g. `EwUTt2eoka-oEV5kf-o0O` +nanoid-simple | 24 | `a-z0-9` | size/24 | use [nanoid/generate](https://github.com/ai/nanoid) generated string.
e.g. `pfldm3gg8h9psydphotqe71d` +nanoid-lowercase | 26 | `a-z` | size/26 | use [nanoid/generate](https://github.com/ai/nanoid) generated string.
e.g. `jsjxoibprplrdoitjmppotjrnm` + + +## Define Custom Generators +This sample shows how to register a generator function. +The generator function should return a function that returns a ID. + +```js +const { register } = require('id-generators'); + +register('my_custom_id', function(option) { + option = option || {}; + let size = option.size || 8; + let prefix = option.prefix || 'items-'; + return function(title) { + return prefix + title.toLowerCase().replace(/[^\w]/g, '').substring(0, size); + }; +}); +``` +```js +const generators = require('id-generators'); +const generator = generators.get('my_custom_id'); +const generate = generator({size: 6}); + +console.log('ID: ' + generate('Hello World!')); +// ID: items-hellow +console.log('ID: ' + generate('Foo Bar!')); +// ID: items-foobar +``` + +## Related +- [Awesome Unique ID](https://github.com/grantcarthew/awesome-unique-id) - A curated list of awesome Unique ID libraries and resources. +- [cuid](https://github.com/ericelliott/cuid) - Collision-resistant ids optimized for horizontal scaling and binary search lookup performance. +- [nanoid](https://github.com/ai/nanoid) - A tiny, secure, URL-friendly, unique string ID generator for JavaScript. + + +## License +Copyright (c) 2019 dailyrandomphoto. Licensed under the [MIT license][license-url]. + +[npm-url]: https://www.npmjs.com/package/id-generators +[travis-url]: https://travis-ci.org/dailyrandomphoto/id-generators +[coveralls-url]: https://coveralls.io/github/dailyrandomphoto/id-generators?branch=master +[license-url]: LICENSE +[dependencies-url]: https://david-dm.org/dailyrandomphoto/id-generators +[devDependencies-url]: https://david-dm.org/dailyrandomphoto/id-generators?type=dev + +[npm-downloads-image]: https://img.shields.io/npm/dm/id-generators.svg +[npm-version-image]: https://img.shields.io/npm/v/id-generators.svg +[license-image]: https://img.shields.io/npm/l/id-generators.svg +[travis-image]: https://img.shields.io/travis/dailyrandomphoto/id-generators/master +[coveralls-image]: https://coveralls.io/repos/github/dailyrandomphoto/id-generators/badge.svg?branch=master +[dependencies-image]: https://david-dm.org/dailyrandomphoto/id-generators/status.svg +[devDependencies-image]: https://david-dm.org/dailyrandomphoto/id-generators/dev-status.svg diff --git a/lib/cuid-slug.js b/lib/cuid-slug.js new file mode 100644 index 0000000..3f9dad6 --- /dev/null +++ b/lib/cuid-slug.js @@ -0,0 +1,7 @@ +'use strict'; + +const {slug} = require('cuid'); + +module.exports = function () { + return () => slug(); +}; diff --git a/lib/cuid.js b/lib/cuid.js new file mode 100644 index 0000000..6e35121 --- /dev/null +++ b/lib/cuid.js @@ -0,0 +1,7 @@ +'use strict'; + +const cuid = require('cuid'); + +module.exports = function () { + return cuid; +}; diff --git a/lib/id-generators.js b/lib/id-generators.js new file mode 100644 index 0000000..9ef5bb7 --- /dev/null +++ b/lib/id-generators.js @@ -0,0 +1,29 @@ +'use strict'; + +const store = {}; + +function register(type, fn) { + if (typeof fn !== 'function') { + throw new TypeError('fn must be a function'); + } + + if (typeof store[type] === 'function') { + throw new TypeError('already registered as type: ' + type); + } + + store[type] = fn; +} + +function get(type) { + type = type || 'default'; + return store[type] || throwIt(new TypeError('Can\'t find generator with type: ' + type)); +} + +function throwIt(err) { + throw err; +} + +module.exports = { + register, + get +}; diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..31e6380 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,16 @@ +'use strict'; + +const {register, get} = require('./id-generators'); +const cuid = require('./cuid'); + +register('default', cuid); +register('cuid', cuid); +register('cuid-slug', require('./cuid-slug')); +register('nanoid', require('./nanoid')); +register('nanoid-simple', require('./nanoid-simple')); +register('nanoid-lowercase', require('./nanoid-lowercase')); + +module.exports = { + register, + get +}; diff --git a/lib/nanoid-lowercase.js b/lib/nanoid-lowercase.js new file mode 100644 index 0000000..a1e799f --- /dev/null +++ b/lib/nanoid-lowercase.js @@ -0,0 +1,13 @@ +'use strict'; + +/** + * https://github.com/ai/nanoid + */ +const generate = require('nanoid/generate'); +const alphabet = 'abcdefghijklmnopqrstuvwxyz'; + +module.exports = function (option) { + option = option || {}; + const size = option.size || 26; + return () => generate(alphabet, size); +}; diff --git a/lib/nanoid-simple.js b/lib/nanoid-simple.js new file mode 100644 index 0000000..e379bf4 --- /dev/null +++ b/lib/nanoid-simple.js @@ -0,0 +1,13 @@ +'use strict'; + +/** + * https://github.com/ai/nanoid + */ +const generate = require('nanoid/generate'); +const alphabet = '0123456789abcdefghijklmnopqrstuvwxyz'; + +module.exports = function (option) { + option = option || {}; + const size = option.size || 24; + return () => generate(alphabet, size); +}; diff --git a/lib/nanoid.js b/lib/nanoid.js new file mode 100644 index 0000000..1281142 --- /dev/null +++ b/lib/nanoid.js @@ -0,0 +1,12 @@ +'use strict'; + +/** + * https://github.com/ai/nanoid + */ +const nanoid = require('nanoid'); + +module.exports = function (option) { + option = option || {}; + const size = option.size || 21; + return () => nanoid(size); +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..fd06d35 --- /dev/null +++ b/package.json @@ -0,0 +1,58 @@ +{ + "name": "id-generators", + "version": "1.0.0", + "description": "id-generators is small JavaScript library to generate ID with awesome Unique ID libraries.", + "main": "lib/index.js", + "scripts": { + "eslint": "xo --fix", + "test": "xo && mocha test/index.js", + "test-cov": "nyc npm run test" + }, + "dependencies": { + "cuid": "^2.1.6", + "nanoid": "^2.1.6" + }, + "devDependencies": { + "chai": "^4.2.0", + "mocha": "^6.2.1", + "nyc": "^14.1.1", + "xo": "^0.25.3" + }, + "keywords": [ + "generate", + "generator", + "id", + "uuid", + "unique-id", + "cuid", + "nanoid", + "url" + ], + "files": [ + "lib/", + "LICENSE", + "README.md" + ], + "engines": { + "node": ">=8.6.0" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/dailyrandomphoto/id-generators.git" + }, + "author": "dailyrandomphoto (https://www.dailyrandomphoto.com/)", + "license": "MIT", + "bugs": { + "url": "https://github.com/dailyrandomphoto/id-generators/issues" + }, + "homepage": "https://github.com/dailyrandomphoto/id-generators#readme", + "xo": { + "space": 2, + "overrides": [ + { + "files": "test/*.js", + "envs": ["node", "mocha"] + } + ] + } +} diff --git a/test/index.js b/test/index.js new file mode 100644 index 0000000..dc5d6de --- /dev/null +++ b/test/index.js @@ -0,0 +1,152 @@ +'use strict'; + +const {expect} = require('chai'); +const generators = require('../lib'); + +describe('id-generators', () => { + it('should throw an error when register with an invalid argument', () => { + expect(() => generators.register('invalid', 'not a function')).to.throw(TypeError); + }); + + it('should throw an error when register with an already registered key', () => { + expect(() => generators.register('cuid', () => {})).to.throw(TypeError); + }); + + it('should use the custom function', () => { + generators.register('my_custom_func', () => { + return () => new Date().getTime(); + }); + + const generator = generators.get('my_custom_func'); + generator.should.be.a('function'); + const gen = generator({}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('number'); + }); + + it('should use option in the custom function', () => { + generators.register('my_custom_path', option => { + option = option || {}; + const size = option.size || 8; + const prefix = option.prefix || 'items-'; + return function (title) { + return prefix + title.toLowerCase().replace(/[^\w]/g, '').slice(0, size); + }; + }); + + const generator = generators.get('my_custom_path'); + generator.should.be.a('function'); + const gen = generator({size: 6}); + gen.should.be.a('function'); + const id = gen('Hello World!'); + console.log(' ID: ' + id); + id.should.eql('items-hellow'); + }); + + it('should throw an error when get a unregistered generator', () => { + expect(() => generators.get('unregistered')).to.throw(TypeError); + }); + + it('should return a default function', () => { + const generator = generators.get(); + generator.should.be.a('function'); + const gen = generator({}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string'); + }); + + describe('cuid', () => { + it('should return a string with length is 25', () => { + const generator = generators.get('cuid'); + generator.should.be.a('function'); + const gen = generator({}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf(25); + id.slice(0, 1).should.eql('c'); + }); + }); + + describe('cuid-slug', () => { + it('should return a string with length is 7 to 10', () => { + const generator = generators.get('cuid-slug'); + generator.should.be.a('function'); + const gen = generator({}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf.within(7, 10); + }); + }); + + describe('nanoid', () => { + it('should return a string with length is 21', () => { + const generator = generators.get('nanoid'); + generator.should.be.a('function'); + const gen = generator({}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf(21); + }); + + it('should return a string with a custom length', () => { + const generator = generators.get('nanoid'); + generator.should.be.a('function'); + const gen = generator({size: 18}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf(18); + }); + }); + + describe('nanoid-simple', () => { + it('should return a string with length is 24', () => { + const generator = generators.get('nanoid-simple'); + generator.should.be.a('function'); + const gen = generator({}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf(24); + }); + + it('should return a string with a custom length', () => { + const generator = generators.get('nanoid-simple'); + generator.should.be.a('function'); + const gen = generator({size: 18}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf(18); + }); + }); + + describe('nanoid-lowercase', () => { + it('should return a string with length is 26', () => { + const generator = generators.get('nanoid-lowercase'); + generator.should.be.a('function'); + const gen = generator({}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf(26); + }); + + it('should return a string with a custom length', () => { + const generator = generators.get('nanoid-lowercase'); + generator.should.be.a('function'); + const gen = generator({size: 18}); + gen.should.be.a('function'); + const id = gen(); + console.log(' ID: ' + id); + id.should.be.a('string').have.lengthOf(18); + }); + }); +}); diff --git a/test/mocha.opts b/test/mocha.opts new file mode 100644 index 0000000..efa7cb0 --- /dev/null +++ b/test/mocha.opts @@ -0,0 +1,6 @@ +--colors +--reporter spec +--ui bdd +--full-trace +--exit +--require chai/register-should