diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index b735373..4de26c7 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,35 +1,35 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Screenshots** -If applicable, add screenshots to help explain your problem. - -**Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] - -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - -**Additional context** -Add any other context about the problem here. +--- +name: Bug report +about: Create a report to help us improve + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 066b2d9..5384295 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,17 +1,17 @@ ---- -name: Feature request -about: Suggest an idea for this project - ---- - -**Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - -**Describe the solution you'd like** -A clear and concise description of what you want to happen. - -**Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - -**Additional context** -Add any other context or screenshots about the feature request here. +--- +name: Feature request +about: Suggest an idea for this project + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 191a0c2..cb9857a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,49 +1,49 @@ -# Contributing to checkif.js - -We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: - -- Reporting a bug -- Discussing the current state of the code -- Submitting a fix -- Proposing new features -- Becoming a maintainer - -## We Develop with Github - -We use github to host code, to track issues and feature requests, as well as accept pull requests. - -## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests - -Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: - -1. Fork the repo and create your branch from `master`. -2. If you've added code that should be tested, add tests (aiming at 100% coverage). -3. If you've changed APIs, update the documentation. -4. Ensure the test suite passes. -5. Make sure your code lints. -6. Issue that pull request! - -## Any contributions you make will be under the MIT Software License - -In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. - -## Report bugs using Github's [issues](https://github.com/joeltankam/checkif.js/issues) - -We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/joeltankam/checkif.js/issues); it's that easy! Make sure to follow the [bug report template](https://github.com/joeltankam/checkif.js/blob/master/.github/ISSUE_TEMPLATE/bug_report.md). - -## Request features using Github's [issues](https://github.com/joeltankam/checkif.js/issues) - -Request a feature by [opening a new issue](https://github.com/joeltankam/checkif.js/issues). Make sure to follow the [feature request template](https://github.com/joeltankam/checkif.js/blob/master/.github/ISSUE_TEMPLATE/feature_request.md). - -## Use a Consistent Coding Style - -- Make sure you're using our `.editorconfig` file -- Make sure you're running `eslint`. You can check the integration with your editor in the [official documentation](https://eslint.org/docs/user-guide/integrations) - -## License - -By contributing, you agree that your contributions will be licensed under its MIT License. - -## References - -This document was adapted from [briandk's Contributing.md](https://gist.github.com/briandk/3d2e8b3ec8daf5a27a62) +# Contributing to checkif.js + +We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: + +- Reporting a bug +- Discussing the current state of the code +- Submitting a fix +- Proposing new features +- Becoming a maintainer + +## We Develop with Github + +We use github to host code, to track issues and feature requests, as well as accept pull requests. + +## We Use [Github Flow](https://guides.github.com/introduction/flow/index.html), So All Code Changes Happen Through Pull Requests + +Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). We actively welcome your pull requests: + +1. Fork the repo and create your branch from `master`. +2. If you've added code that should be tested, add tests (aiming at 100% coverage). +3. If you've changed APIs, update the documentation. +4. Ensure the test suite passes. +5. Make sure your code lints. +6. Issue that pull request! + +## Any contributions you make will be under the MIT Software License + +In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. + +## Report bugs using Github's [issues](https://github.com/joeltankam/checkif.js/issues) + +We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/joeltankam/checkif.js/issues); it's that easy! Make sure to follow the [bug report template](https://github.com/joeltankam/checkif.js/blob/master/.github/ISSUE_TEMPLATE/bug_report.md). + +## Request features using Github's [issues](https://github.com/joeltankam/checkif.js/issues) + +Request a feature by [opening a new issue](https://github.com/joeltankam/checkif.js/issues). Make sure to follow the [feature request template](https://github.com/joeltankam/checkif.js/blob/master/.github/ISSUE_TEMPLATE/feature_request.md). + +## Use a Consistent Coding Style + +- Make sure you're using our `.editorconfig` file +- Make sure you're running `eslint`. You can check the integration with your editor in the [official documentation](https://eslint.org/docs/user-guide/integrations) + +## License + +By contributing, you agree that your contributions will be licensed under its MIT License. + +## References + +This document was adapted from [briandk's Contributing.md](https://gist.github.com/briandk/3d2e8b3ec8daf5a27a62) diff --git a/LICENSE b/LICENSE index 430cc3b..0936cb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,21 @@ -MIT License - -Copyright (c) 2018 Joël TANKAM - -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. +MIT License + +Copyright (c) 2018 Joël TANKAM + +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 index 3125c52..1f47d7a 100644 --- a/README.md +++ b/README.md @@ -220,7 +220,36 @@ is.windowObject(dom.window); // true This group of methods allows to verify _if_ a given string _is_ from a specific known type of value. -_To be implemented_ +##### `email(value)` +Check _if_ a given string _is_ an email. This verification is done according to [RFC5322](https://tools.ietf.org/html/rfc5322#section-3.2.3) official standard. + +```js +is.email('simple@example.com') // true +is.email('very.common@example.com') // true +is.email('disposable.style.email.with+symbol@example.com') // true +is.email('other.email-with-hyphen@example.com') // true +is.email('fully-qualified-domain@example.com') // true +is.email('user.name+tag+sorting@example.com') // true +is.email('x@example.com') // true +is.email('example-indeed@strange-example.com') // true +is.email('example@s.example') // true +is.email('" "@example.org') // true +is.email('"john..doe"@example.com') // true +is.email('example.with.ip@[192.168.2.1]') // true +is.email('"with(comment)@example.com') // true +is.email('"with@(comment)example.com') // true + +is.email('Abc.example.com') // false, no @ character +is.email('@example.com') // false, no local part +is.email('A@b@c@example.com') // false, only one @ is allowed outside quotation marks +is.email('a"b(c)d,e:f;gi[j\k]l@example.com') // false, none of the special characters in this local-part are allowed outside quotation marks +is.email('just"not"right@example.com') // false, quoted strings must be dot separated or the only element making up the local-part +is.email('this is"not\allowed@example.com') // false, spaces, quotes, and backslashes may only exist when within quoted strings and preceded by a backslash +is.email('this\ still\"not\\allowed@example.com') // false, even if escaped (preceded by a backslash), spaces, quotes, and backslashes must still be contained by quotes +is.email('1234567890123456789012345678901234567890123456789012345678901234+x@example.com') // false, local part is longer than 64 characters +is.email('john..doe@example.com') // false, double dot before @ +is.email('john.doe@example..com') // false, double dot after @ +``` #### String diff --git a/package-lock.json b/package-lock.json index 5c84e1e..e00fd3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2721,7 +2721,8 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "aproba": { "version": "1.2.0", @@ -2742,12 +2743,14 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, + "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -2762,17 +2765,20 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "core-util-is": { "version": "1.0.2", @@ -2889,7 +2895,8 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "ini": { "version": "1.3.5", @@ -2901,6 +2908,7 @@ "version": "1.0.0", "bundled": true, "dev": true, + "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -2915,6 +2923,7 @@ "version": "3.0.4", "bundled": true, "dev": true, + "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -2922,12 +2931,14 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "minipass": { "version": "2.2.4", "bundled": true, "dev": true, + "optional": true, "requires": { "safe-buffer": "^5.1.1", "yallist": "^3.0.0" @@ -2946,6 +2957,7 @@ "version": "0.5.1", "bundled": true, "dev": true, + "optional": true, "requires": { "minimist": "0.0.8" } @@ -3026,7 +3038,8 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "object-assign": { "version": "4.1.1", @@ -3038,6 +3051,7 @@ "version": "1.4.0", "bundled": true, "dev": true, + "optional": true, "requires": { "wrappy": "1" } @@ -3123,7 +3137,8 @@ "safe-buffer": { "version": "5.1.1", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "safer-buffer": { "version": "2.1.2", @@ -3159,6 +3174,7 @@ "version": "1.0.2", "bundled": true, "dev": true, + "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -3178,6 +3194,7 @@ "version": "3.0.1", "bundled": true, "dev": true, + "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -3221,12 +3238,14 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true }, "yallist": { "version": "3.0.2", "bundled": true, - "dev": true + "dev": true, + "optional": true } } }, diff --git a/src/is/index.js b/src/is/index.js index 853e176..2ae5b3a 100644 --- a/src/is/index.js +++ b/src/is/index.js @@ -1,11 +1,13 @@ import types from './types'; -import arithmetic from './arithmetic'; +import regexp from './regexp'; import string from './string'; +import arithmetic from './arithmetic'; import datetime from './datetime'; export default { ...types, - ...arithmetic, + ...regexp, ...string, + ...arithmetic, ...datetime, }; diff --git a/src/is/regexp.js b/src/is/regexp.js new file mode 100644 index 0000000..0d7f8e1 --- /dev/null +++ b/src/is/regexp.js @@ -0,0 +1,39 @@ +import { isString, isNull } from './types'; + +function isSimpleEmailLocalPart(value) { + const localPartPattern = /^([a-z]|\d|[!#$%&'*+\-/=?^_`{|}~])+(\.([a-z]|\d|[!#$%&'*+\-/=?^_`{|}~])+)*$/; + return localPartPattern.test(value); +} +function isQuotedEmailLocalPart(value) { + const localPartPattern = /^(\")((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e])|(\\[\x01-\x09\x0b\x0c\x0d-\x7f])))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\")$/i + return localPartPattern.test(value); +} +function isEmailLocalPart(value) { + return isSimpleEmailLocalPart(value) || isQuotedEmailLocalPart(value); +} +function isEmailDomain(value) { + const emailDomainPattern = /^((([a-z]|\d)|(([a-z]|\d)([a-z]|\d|-|\.|_|~)*([a-z]|\d)))\.)+(([a-z])|(([a-z])([a-z]|\d|-|\.|_|~)*([a-z])))$/; + return false; +} + +/** + * Determines whether a given string is an email + * @param {*} value the string to check + * @returns {Boolean} `true` if the string is an email; otherwise, `false` + */ +export function isEmail(value) { + if (!isString(value)) return false; + const globalEmailPattern = /^(.+)@(.+)$/i; + const [, localPart, domain] = globalEmailPattern.exec(value) || [null, null]; + if (isNull(localPart) + || isNull(domain) + || localPart.length === 0 + || localPart.length > 255 + || domain.length === 0 + || domain.length > 63) return false; + return isEmailLocalPart(localPart) && isEmailDomain(domain); +} + +export default { + email: isEmail, +}; diff --git a/test.js b/test.js new file mode 100644 index 0000000..258cab1 --- /dev/null +++ b/test.js @@ -0,0 +1,3 @@ +var check = require("./dist/checkif"); + +console.log(check); \ No newline at end of file diff --git a/test/is/regexp.test.js b/test/is/regexp.test.js new file mode 100644 index 0000000..c8fafc1 --- /dev/null +++ b/test/is/regexp.test.js @@ -0,0 +1,36 @@ +import is from '../../src/is/regexp'; +import { testFalsyWithNullable } from '../utils'; +import { testIntegrationWithAllHasCheckers } from '../has.test'; + +describe('isEmail', () => { + test('returns true on email strings', () => { + expect(is.email('simple@example.com')).toBeTruthy(); + expect(is.email('very.common@example.com')).toBeTruthy(); + expect(is.email('disposable.style.email.with+symbol@example.com')).toBeTruthy(); + expect(is.email('other.email-with-hyphen@example.com')).toBeTruthy(); + expect(is.email('fully-qualified-domain@example.com')).toBeTruthy(); + expect(is.email('user.name+tag+sorting@example.com')).toBeTruthy(); + expect(is.email('x@example.com')).toBeTruthy(); + expect(is.email('example-indeed@strange-example.com')).toBeTruthy(); + expect(is.email('example@s.example')).toBeTruthy(); + expect(is.email('" "@example.org')).toBeTruthy(); + expect(is.email('"john..doe"@example.com')).toBeTruthy(); + expect(is.email('example.with.ip@[192.168.2.1')).toBeTruthy(); + expect(is.email('with(comment)@example.com')).toBeTruthy(); + expect(is.email('with@(comment)example.com')).toBeTruthy(); + }); + test('returns false on anything else', () => { + expect(is.email('Abc.example.com')).toBeFalsy(); + expect(is.email('@example.com')).toBeFalsy(); + expect(is.email('A@b@c@example.com')).toBeFalsy(); + expect(is.email('a"b(c)d,e:f;gi[j\k]l@example.com')).toBeFalsy(); + expect(is.email('just"not"right@example.com')).toBeFalsy(); + expect(is.email('this is"not\allowed@example.com')).toBeFalsy(); + expect(is.email('this\ still\"not\\allowed@example.com')).toBeFalsy(); + expect(is.email('1234567890123456789012345678901234567890123456789012345678901234+x@example.com')).toBeFalsy(); + expect(is.email('john..doe@example.com')).toBeFalsy(); + expect(is.email('john.doe@example..com')).toBeFalsy(); + }); + testIntegrationWithAllHasCheckers(is.email, 'simple@example.com', '@not.email', 'not.email'); + testFalsyWithNullable(is.email); +});