├── .gitignore ├── .npmignore ├── .travis.yml ├── index.d.ts ├── index.js ├── license ├── package-lock.json ├── package.json ├── readme.md └── test └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .npmignore 2 | .gitignore 3 | .travis.yml 4 | package-lock.json 5 | node_modules/ 6 | .git/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 10 4 | - 8 5 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "normalize-email" { 2 | /** 3 | * Normalize emails for uniqueness validation 4 | * 5 | * @param email Email to normalize 6 | */ 7 | function normalizeEmail(email: string): string; 8 | export default normalizeEmail; 9 | } 10 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var PLUS_ONLY = /\+.*$/; 4 | var PLUS_AND_DOT = /\.|\+.*$/g; 5 | var normalizeableProviders = { 6 | 'gmail.com': { 7 | 'cut': PLUS_AND_DOT 8 | }, 9 | 'googlemail.com': { 10 | 'cut': PLUS_AND_DOT, 11 | 'aliasOf': 'gmail.com' 12 | }, 13 | 'hotmail.com': { 14 | 'cut': PLUS_ONLY 15 | }, 16 | 'live.com': { 17 | 'cut': PLUS_AND_DOT 18 | }, 19 | 'outlook.com': { 20 | 'cut': PLUS_ONLY 21 | } 22 | }; 23 | 24 | module.exports = function normalizeEmail(eMail) { 25 | if (typeof eMail != 'string') { 26 | throw new TypeError('normalize-email expects a string'); 27 | } 28 | 29 | var email = eMail.toLowerCase(); 30 | var emailParts = email.split(/@/); 31 | 32 | if (emailParts.length !== 2) { 33 | return eMail; 34 | } 35 | 36 | var username = emailParts[0]; 37 | var domain = emailParts[1]; 38 | 39 | if (normalizeableProviders.hasOwnProperty(domain)) { 40 | if (normalizeableProviders[domain].hasOwnProperty('cut')) { 41 | username = username.replace(normalizeableProviders[domain].cut, ''); 42 | } 43 | if (normalizeableProviders[domain].hasOwnProperty('aliasOf')) { 44 | domain = normalizeableProviders[domain].aliasOf; 45 | } 46 | } 47 | 48 | return username + '@' + domain; 49 | } 50 | -------------------------------------------------------------------------------- /license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 John Otander 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "normalize-email", 3 | "version": "1.1.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "balanced-match": { 8 | "version": "1.0.0", 9 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 10 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 11 | "dev": true 12 | }, 13 | "brace-expansion": { 14 | "version": "1.1.11", 15 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 16 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 17 | "dev": true, 18 | "requires": { 19 | "balanced-match": "^1.0.0", 20 | "concat-map": "0.0.1" 21 | } 22 | }, 23 | "browser-stdout": { 24 | "version": "1.3.1", 25 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", 26 | "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", 27 | "dev": true 28 | }, 29 | "commander": { 30 | "version": "2.15.1", 31 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", 32 | "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", 33 | "dev": true 34 | }, 35 | "concat-map": { 36 | "version": "0.0.1", 37 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 38 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 39 | "dev": true 40 | }, 41 | "debug": { 42 | "version": "3.1.0", 43 | "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", 44 | "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", 45 | "dev": true, 46 | "requires": { 47 | "ms": "2.0.0" 48 | } 49 | }, 50 | "diff": { 51 | "version": "3.5.0", 52 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", 53 | "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", 54 | "dev": true 55 | }, 56 | "escape-string-regexp": { 57 | "version": "1.0.5", 58 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 59 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 60 | "dev": true 61 | }, 62 | "fs.realpath": { 63 | "version": "1.0.0", 64 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 65 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 66 | "dev": true 67 | }, 68 | "glob": { 69 | "version": "7.1.2", 70 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", 71 | "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", 72 | "dev": true, 73 | "requires": { 74 | "fs.realpath": "^1.0.0", 75 | "inflight": "^1.0.4", 76 | "inherits": "2", 77 | "minimatch": "^3.0.4", 78 | "once": "^1.3.0", 79 | "path-is-absolute": "^1.0.0" 80 | } 81 | }, 82 | "growl": { 83 | "version": "1.10.5", 84 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", 85 | "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", 86 | "dev": true 87 | }, 88 | "has-flag": { 89 | "version": "3.0.0", 90 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", 91 | "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", 92 | "dev": true 93 | }, 94 | "he": { 95 | "version": "1.1.1", 96 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 97 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 98 | "dev": true 99 | }, 100 | "inflight": { 101 | "version": "1.0.6", 102 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 103 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 104 | "dev": true, 105 | "requires": { 106 | "once": "^1.3.0", 107 | "wrappy": "1" 108 | } 109 | }, 110 | "inherits": { 111 | "version": "2.0.3", 112 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 113 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", 114 | "dev": true 115 | }, 116 | "minimatch": { 117 | "version": "3.0.4", 118 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 119 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 120 | "dev": true, 121 | "requires": { 122 | "brace-expansion": "^1.1.7" 123 | } 124 | }, 125 | "minimist": { 126 | "version": "0.0.8", 127 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 128 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 129 | "dev": true 130 | }, 131 | "mkdirp": { 132 | "version": "0.5.1", 133 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 134 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 135 | "dev": true, 136 | "requires": { 137 | "minimist": "0.0.8" 138 | } 139 | }, 140 | "mocha": { 141 | "version": "5.2.0", 142 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", 143 | "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", 144 | "dev": true, 145 | "requires": { 146 | "browser-stdout": "1.3.1", 147 | "commander": "2.15.1", 148 | "debug": "3.1.0", 149 | "diff": "3.5.0", 150 | "escape-string-regexp": "1.0.5", 151 | "glob": "7.1.2", 152 | "growl": "1.10.5", 153 | "he": "1.1.1", 154 | "minimatch": "3.0.4", 155 | "mkdirp": "0.5.1", 156 | "supports-color": "5.4.0" 157 | } 158 | }, 159 | "ms": { 160 | "version": "2.0.0", 161 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 162 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", 163 | "dev": true 164 | }, 165 | "once": { 166 | "version": "1.4.0", 167 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 168 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 169 | "dev": true, 170 | "requires": { 171 | "wrappy": "1" 172 | } 173 | }, 174 | "path-is-absolute": { 175 | "version": "1.0.1", 176 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 177 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 178 | "dev": true 179 | }, 180 | "supports-color": { 181 | "version": "5.4.0", 182 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", 183 | "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", 184 | "dev": true, 185 | "requires": { 186 | "has-flag": "^3.0.0" 187 | } 188 | }, 189 | "wrappy": { 190 | "version": "1.0.2", 191 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 192 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 193 | "dev": true 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "normalize-email", 3 | "description": "Normalize + and . emails for uniqueness validation", 4 | "author": "John Otander", 5 | "version": "1.1.1", 6 | "main": "index.js", 7 | "types": "index.d.ts", 8 | "directories": { 9 | "test": "test" 10 | }, 11 | "scripts": { 12 | "test": "mocha test" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/johnotander/normalize-email.git" 17 | }, 18 | "keywords": [ 19 | "email", 20 | "normalize", 21 | "gmail", 22 | "hotmail", 23 | "live", 24 | "outlook" 25 | ], 26 | "license": "MIT", 27 | "bugs": { 28 | "url": "https://github.com/johnotander/normalize-email/issues" 29 | }, 30 | "homepage": "https://github.com/johnotander/normalize-email", 31 | "devDependencies": { 32 | "mocha": "*" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # normalize-email [![Build Status](https://secure.travis-ci.org/johnotander/normalize-email.png?branch=master)](https://travis-ci.org/johnotander/normalize-email) 2 | 3 | Normalize emails for uniqueness validation. This will convert the email to lowercase, remove dots (`.`), and plus signs 4 | followed by arbitrary strings (`+foobar`). 5 | 6 | Based on [normailize](https://github.com/soundcloud/normailize). 7 | 8 | ## Installation 9 | 10 | ```bash 11 | npm install --save normalize-email 12 | ``` 13 | 14 | ## Usage 15 | 16 | ```javascript 17 | var normalizeEmail = require('normalize-email') 18 | 19 | normalizeEmail('johnotander@GMAIL.com') // => 'johnotander@gmail.com' 20 | normalizeEmail('john.o.t.a.n.d.e.r@gmail.com') // => 'johnotander@gmail.com' 21 | normalizeEmail('johnotander@googlemail.com') // => 'johnotander@gmail.com' 22 | normalizeEmail('johnotander+foobar@gmail.com') // => 'johnotander@gmail.com' 23 | normalizeEmail('JOHN.OTANDER+OHAI@gmail.com') // => 'johnotander@gmail.com' 24 | ``` 25 | 26 | ## Normalization Rules 27 | 28 | Normalization rules vary by domain. The rules implemented by this package are: 29 | 1. "plus" — remove first `+` mark and anything after it 30 | 1. "dot" — remove all `.` marks 31 | 1. "none" — do nothing 32 | 33 | Emails are normalized by domain as follows: 34 | 1. `gmail.com` — plus and dot 35 | 1. `googlemail.com` (alias of gmail.com) — plus and dot 36 | 1. hotmail.com — plus only 37 | 1. live.com — plus and dot 38 | 1. outlook.com — plus only 39 | 1. all other domains — none 40 | 41 | ## License 42 | 43 | MIT 44 | 45 | ## Contributing 46 | 47 | 1. Fork it 48 | 2. Create your feature branch (`git checkout -b my-new-feature`) 49 | 3. Commit your changes (`git commit -am 'Add some feature'`) 50 | 4. Push to the branch (`git push origin my-new-feature`) 51 | 5. Create new Pull Request 52 | 53 | Crafted with <3 by John Otander ([@4lpine](https://twitter.com/4lpine)). 54 | 55 | *** 56 | 57 | > This package was initially generated with [yeoman](http://yeoman.io) and the [p generator](https://github.com/johnotander/generator-p.git). 58 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert') 2 | var normalizeEmail = require('..') 3 | 4 | var gmailEmailsToNormalize = [ 5 | 'johnotander@gmail.com', 6 | 'johnotander@googlemail.com', 7 | 'johnotander@GMAIL.com', 8 | 'johnotander+foobar@gmail.com', 9 | 'john.o.t.a.n.d.er+foobar@gmail.com', 10 | 'JOHN.o.t.a.n.d.er+foobar@googlemail.com', 11 | 'john.otander@gmail.com' 12 | ] 13 | 14 | var hotmailEmailsToNormalize = [ 15 | 'johnotander@hotmail.com', 16 | 'johnotander@hotmail.com', 17 | 'johnotander@HOTMAIL.com', 18 | 'Johnotander@hotmail.com' 19 | ] 20 | 21 | var liveEmailsToNormalize = [ 22 | 'johnotander@live.com', 23 | 'johnotander@live.com', 24 | 'johnotander@live.com', 25 | 'johnotander+foobar@live.com', 26 | 'john.o.t.a.n.d.er+foobar@live.com', 27 | 'JOHN.o.t.a.n.d.er+foobar@live.com', 28 | 'john.otander@live.com' 29 | ] 30 | 31 | var outlookEmailsToNormalize = [ 32 | 'john.otander@outlook.com', 33 | 'JOHN.otander@outlook.com', 34 | 'john.Otander+any.label@outlook.com', 35 | 'john.otander+foobar@outlook.com', 36 | ] 37 | 38 | describe('normalize-email', function() { 39 | 40 | it('should normalize gmail emails', function() { 41 | gmailEmailsToNormalize.forEach(function(email) { 42 | assert.equal(normalizeEmail(email), 'johnotander@gmail.com') 43 | }) 44 | }) 45 | 46 | it('should normalize hotmail emails', function() { 47 | hotmailEmailsToNormalize.forEach(function(email) { 48 | assert.equal(normalizeEmail(email), 'johnotander@hotmail.com') 49 | }) 50 | }) 51 | 52 | it('should not remove dots from hotmail emails', function() { 53 | assert.equal(normalizeEmail('john.otander@hotmail.com'), 'john.otander@hotmail.com') 54 | }) 55 | 56 | it('should normalize live emails', function() { 57 | liveEmailsToNormalize.forEach(function(email) { 58 | assert.equal(normalizeEmail(email), 'johnotander@live.com') 59 | }) 60 | }) 61 | 62 | it('should normalize outlook emails', function() { 63 | outlookEmailsToNormalize.forEach(function(email) { 64 | assert.equal(normalizeEmail(email), 'john.otander@outlook.com') 65 | }) 66 | }) 67 | 68 | }) 69 | --------------------------------------------------------------------------------