From 32dd964213e1a17b43133a8313b85cf679352cc5 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 01:45:41 -0400 Subject: [PATCH 01/27] Update: description for breakdown to be more forward --- src/breakdown.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/breakdown.js b/src/breakdown.js index 850023d..e990551 100644 --- a/src/breakdown.js +++ b/src/breakdown.js @@ -6,7 +6,7 @@ import uglify from './uglify.js' * @function * @category Function * @sig String -> String -> Object - * @description Takes a provided phone string and breaks it down into an object of codes + * @description Takes a provided phone string and breaks it down into an object of codes only works loosely for NANP numbers. The gatcha here is that NANP numbers take the form of NXX NXX XXXX where N is a digit from 2-9 and X is a digit from 0-9, but in order to support placeholders we use a [_0-9]{3} check * @param {String} phone The phone number to breakdown * @return {Object} Returns an object of the broken down phone number * From f6dfb31dd7e10aaaaa7266a6408f1fbddf5124ae Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 01:46:31 -0400 Subject: [PATCH 02/27] Update: Rename unit test files --- tests/{breakdown.js => breakdown.spec.js} | 0 tests/{format.js => format.spec.js} | 7 +++++++ tests/{isValid.js => isValid.spec.js} | 12 ++++++++++++ tests/{main.js => main.spec.js} | 0 tests/{uglify.js => uglify.spec.js} | 6 ++++++ 5 files changed, 25 insertions(+) rename tests/{breakdown.js => breakdown.spec.js} (100%) rename tests/{format.js => format.spec.js} (91%) rename tests/{isValid.js => isValid.spec.js} (66%) rename tests/{main.js => main.spec.js} (100%) rename tests/{uglify.js => uglify.spec.js} (69%) diff --git a/tests/breakdown.js b/tests/breakdown.spec.js similarity index 100% rename from tests/breakdown.js rename to tests/breakdown.spec.js diff --git a/tests/format.js b/tests/format.spec.js similarity index 91% rename from tests/format.js rename to tests/format.spec.js index 7ed0d0a..ac8d40b 100644 --- a/tests/format.js +++ b/tests/format.spec.js @@ -100,6 +100,13 @@ test('Catches letters when passed in', t => { t.end() }) +test('Handles unordinary phone numbers', t => { + const results = format('NNN NNN NN NN NN', '046123456789') + + t.same(results, '046 123 45 67 89') + t.end() +}) + test('Supports Placeholder characters', t => { const fn = format('NNN-NNN-NNNN') diff --git a/tests/isValid.js b/tests/isValid.spec.js similarity index 66% rename from tests/isValid.js rename to tests/isValid.spec.js index dde53b8..ec8a267 100644 --- a/tests/isValid.js +++ b/tests/isValid.spec.js @@ -19,6 +19,18 @@ test('Test Country Code', t => { t.end() }) +test('Test extension', t => { + t.ok(isValid('555-444-3333 ext 123'), 'Handles extension') + t.ok(isValid('555-444-3333 x 123'), 'Handles extension with x') + t.end() +}) + +test('Test unordinary phone numbers', t => { + t.ok(isValid('046 123 456 789'), 'Handles spaces') + t.ok(isValid('046 123 45 67 89'), 'Handles spaces with less numbers') + t.end() +}) + test('Test invalid type', t => { t.notOk(isValid('555444666'), 'Handles invalid length') t.notOk(isValid('(555)-444-666'), 'Handles invalid length') diff --git a/tests/main.js b/tests/main.spec.js similarity index 100% rename from tests/main.js rename to tests/main.spec.js diff --git a/tests/uglify.js b/tests/uglify.spec.js similarity index 69% rename from tests/uglify.js rename to tests/uglify.spec.js index 0c4fae7..5cbac8a 100644 --- a/tests/uglify.js +++ b/tests/uglify.spec.js @@ -14,6 +14,12 @@ test('Handles when numbers are thrown at it', t => { t.end() }) +test('Handles when out of ordinary phone numbers are thrown at it', t => { + t.same(uglify('046 123 456 789'), '046123456789') + t.same(uglify('046 123 45 67 89'), '046123456789') + t.end() +}) + // test('Uglify placeholders', t => { // console.log(uglify('__________')) // t.same(uglify('__________'), '') From b0b3b23e6c577e564868a13acc82f8ac8e92ed6f Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 01:46:51 -0400 Subject: [PATCH 03/27] Added: New normalize function --- src/normalize.js | 24 ++++++++++++++++++++++++ tests/normalize.spec.js | 29 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 src/normalize.js create mode 100644 tests/normalize.spec.js diff --git a/src/normalize.js b/src/normalize.js new file mode 100644 index 0000000..5b49cb0 --- /dev/null +++ b/src/normalize.js @@ -0,0 +1,24 @@ +/** + * @name normalize + * @since v4.1.0 + * @function + * @category Function + * @sig String -> String + * @description Strips all of the special characters from the given string but leaves extension and country code characters in place + * @param {String} phone The phone number to trim and strip down + * @return {String} Returns the newly created phone number string + * + * @example + * import { normalize } from 'phone-fns' + * + * normalize('555-444-3333') // => '5554443333' + * normalize('5554443333') // => '5554443333' + * normalize('555.444.3333 x 123') // => '5554443333x123' + */ +export default function normalize (phone) { + if (!phone) { + return '' + } + + return phone.replace(/[\s.\-()]/g, '').trim() +} diff --git a/tests/normalize.spec.js b/tests/normalize.spec.js new file mode 100644 index 0000000..01aff33 --- /dev/null +++ b/tests/normalize.spec.js @@ -0,0 +1,29 @@ +import test from 'tape' +import normalize from '../src/normalize.js' + +test('normalize removes spaces, dots, dashes, and parentheses', (t) => { + t.equal(normalize('123 456 7890'), '1234567890', 'should remove spaces') + t.equal(normalize('123.456.7890'), '1234567890', 'should remove dots') + t.equal(normalize('123-456-7890'), '1234567890', 'should remove dashes') + t.equal(normalize('(123) 456-7890'), '1234567890', 'should remove parentheses') + t.equal(normalize('(123) 456-7890 x123'), '1234567890x123', 'should handle extension') + t.equal(normalize('(123) 456-7890 x 123'), '1234567890x123', 'should handle extension') + t.end() +}) + +test('normalize trims the input', (t) => { + t.equal(normalize(' 1234567890 '), '1234567890', 'should trim leading and trailing spaces') + t.equal(normalize('\t1234567890\t'), '1234567890', 'should trim leading and trailing tabs') + t.end() +}) + +test('normalize handles mixed characters', (t) => { + t.equal(normalize(' (123) 456-7890. '), '1234567890', 'should remove mixed characters and trim') + t.end() +}) + +test('normalize handles empty and null input', (t) => { + t.equal(normalize(''), '', 'should handle empty string') + t.equal(normalize(null), '', 'should handle null input') + t.end() +}) From 77223f53f4a2173e5821b957dff69fc3193f3dcb Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:18:54 -0400 Subject: [PATCH 04/27] Added: validate function --- src/validate.js | 33 ++++++++++++++++++++++++ tests/validate.spec.js | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 src/validate.js create mode 100644 tests/validate.spec.js diff --git a/src/validate.js b/src/validate.js new file mode 100644 index 0000000..b4f68e0 --- /dev/null +++ b/src/validate.js @@ -0,0 +1,33 @@ +import normalize from './normalize.js' +import uglify from './uglify.js' + +/** + * @name validate + * @since v4.1.0 + * @function + * @category Function + * @sig String -> Boolean + * @description + * Validates the base number, strips out special characters and spaces upon validation, can handle country code and extension in the phone number + * @param {String} phone The phone number we want to validate + * @return {Boolean} Returns a boolean if the number provided is valid or not + * @example + * import { validate } from 'phone-fns' + * + * validate('555-444-3333') // => true + * validate('5555') // => false + * validate('5554443333') // => true + * validate('5554443333 x 123') // => true + */ +export default function validate (phone) { + const normPhone = normalize(String(phone)) + const phoneRegex = /^(\+?\d{1,4})?[\s\-.]?\(?\d{1,4}\)?[\s\-.]?\d{1,4}[\s\-.]?\d{1,4}[\s\-.]?\d{1,9}(?:[\s\-.]?(?:x|ext)?\d{1,5})?$/i + + // Validate the length of the number without the ext or country code symbols inlcuded + // (strips the + symbol and the ext/x symbols) + if (uglify(normPhone).length > 15 || uglify(normPhone).length < 10) { + return false + } + + return phoneRegex.test(normPhone) +} diff --git a/tests/validate.spec.js b/tests/validate.spec.js new file mode 100644 index 0000000..0440715 --- /dev/null +++ b/tests/validate.spec.js @@ -0,0 +1,58 @@ +import test from 'tape' +import validate from '../src/validate.js' + +test('Test simple type', t => { + t.ok(validate('555-444-3333'), 'Results returned back ok') + t.ok(validate('4445556666')) + t.ok(validate(4445556666)) + t.end() +}) + +test('Test complex type', t => { + t.ok(validate('(555) 444 3333'), 'Results returned back ok') + t.end() +}) + +test('Test handles wide range of phone numbers', t => { + const validPhoneNumbers = [ + '010.1234.5678', // Number with dots + '10 9876 5432', // South Korea number without country code + '+82 2 3456 7890', // South Korea number with country code + '+82 10 9876 5432', // South Korea mobile number with country code + '+1 (555) 444-3333', // US number with country code + '+44 20 7946 0958', // UK landline with country code + '07911 123456', // UK mobile (domestic) + '+49 30 12345678', // German number with country code + '+1 800 123 4567 x123', // US toll-free number with extension + '+61 2 1234 5678', // Australian number with country code, + '+39 02 1234 5678', // Italian number with country code + '+91 12345 67890' // Indian number with country code + ] + + validPhoneNumbers.forEach(phone => { + t.ok(validate(phone), `Valid phone number: ${phone}`) + }) + t.end() +}) + +test('Handles Invalid range of phone numbers', t => { + const invalidPhoneNumbers = [ + '123', // Too short + '010-1234', // Missing last part of mobile number + '02-3456', // Incomplete landline number + '02-3456-789', // Incomplete landline number (should be 7 digits) + '+82 10', // Too short, incomplete mobile number + '+82 02-1234', // Incomplete landline number + '010-XYZ-1234', // Invalid characters (letters in the mobile number) + '12345', // Too short + 'abcd-efgh-ijkl', // Completely invalid characters + '+44 (0)20 7946', // Incomplete UK number + '+1-555-abc-1234', // Invalid characters + '+12345678901234567890' // Too long + ] + + invalidPhoneNumbers.forEach(phone => { + t.notOk(validate(phone), `Invalid phone number: ${phone}`) + }) + t.end() +}) From 952607ce5af059e7af6cb8b07bbcdc25f73ffbd1 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:19:12 -0400 Subject: [PATCH 05/27] Added: isValidWithFormat function --- src/isValidWithFormat.js | 40 +++++++++++++++++++++++++++++++++ tests/isValidWithFormat.spec.js | 20 +++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/isValidWithFormat.js create mode 100644 tests/isValidWithFormat.spec.js diff --git a/src/isValidWithFormat.js b/src/isValidWithFormat.js new file mode 100644 index 0000000..2e9f194 --- /dev/null +++ b/src/isValidWithFormat.js @@ -0,0 +1,40 @@ +import _curry2 from './_internals/_curry2.js' +import validate from './validate.js' + +/** + * @name isValidWithFormat + * @since v4.1.0 + * @function + * @category Function + * @sig String -> String -> Boolean + * @description + * Validates a phone number based on a custom format provided + * @param {String} phone The phone number to breakdown + * @param {String} format The format to validate against + * @return {Boolean} Returns a boolean if the number provided is valid or not + * @example + * import { isValidWithFormat } from 'phone-fns' + * + * isValidWithFormat('NNN-NNN-NNNN', '123-456-7890') // => true + * isValidWithFormat('NNN-NNN-NNNN', '010-XYZ-1234') // => false + * + * // its also curried + * const fn = isValidWithFormat('NNN-NNN-NNNN') + * fn('123-456-7890') // => true + * fn('010-XYZ-1234') // => false + */ +function isValidWithFormat (format, phone) { + if (phone.length !== format.length) { + return false + } + + for (let i = 0; i < format.length; i++) { + if (format[i] === 'N' && isNaN(phone[i])) { + return false + } + } + + return validate(phone) +} + +export default _curry2(isValidWithFormat) diff --git a/tests/isValidWithFormat.spec.js b/tests/isValidWithFormat.spec.js new file mode 100644 index 0000000..c220a6f --- /dev/null +++ b/tests/isValidWithFormat.spec.js @@ -0,0 +1,20 @@ +import test from 'tape' +import isValidWithFormat from '../src/isValidWithFormat.js' + +test('isValidWithFormat', (t) => { + t.same(isValidWithFormat('NNNNNNNNNN', '1234567890'), true, 'Valid phone number with correct format') + t.same(isValidWithFormat('NNN-NNN-NNNN', '123-456-7890'), true, 'Valid phone number with dashes in correct format') + t.same(isValidWithFormat('NNN-NNN-NNNN', '1234567890'), false, 'Invalid phone number without dashes for dashed format') + t.same(isValidWithFormat('NNNNNNNNNN', '123-456-7890'), false, 'Invalid phone number with dashes for non-dashed format') + t.same(isValidWithFormat('NNNNNNNNNN', '123456789'), false, 'Invalid phone number with incorrect length') + t.same(isValidWithFormat('NNN-NNNN-NNNN', '010-1234--5678'), false, 'Invalid phone number with extra dashes') + t.same(isValidWithFormat('NNNNNNNNNN', 'abcdefghij'), false, 'Invalid phone number with letters') + t.same(isValidWithFormat('NNN-NNN-NNNN', '010-XYZ-1234'), false, 'Invalid phone number with letters') + t.end() +}) + +test('isValidWithFormat with country code and extension', (t) => { + t.same(isValidWithFormat('+1 NNN-NNN-NNNN', '+1 234-567-1890'), true, 'Valid phone number with country code') + t.same(isValidWithFormat('+1 NNN-NNN-NNNN x NNN', '+1 234-567-1890 x 123'), true, 'Valid phone number with country code and extension') + t.end() +}) From 521c92523509f83bd330fb36cdd64fbc6be16086 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:19:42 -0400 Subject: [PATCH 06/27] Fixed: Param order in docs --- src/isValidWithFormat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isValidWithFormat.js b/src/isValidWithFormat.js index 2e9f194..ddaa03a 100644 --- a/src/isValidWithFormat.js +++ b/src/isValidWithFormat.js @@ -9,8 +9,8 @@ import validate from './validate.js' * @sig String -> String -> Boolean * @description * Validates a phone number based on a custom format provided - * @param {String} phone The phone number to breakdown * @param {String} format The format to validate against + * @param {String} phone The phone number to breakdown * @return {Boolean} Returns a boolean if the number provided is valid or not * @example * import { isValidWithFormat } from 'phone-fns' From 1ba7765e22311680e786f124d0f298b05aa13942 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:22:47 -0400 Subject: [PATCH 07/27] Removed: commented out test on uglify --- tests/uglify.spec.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/uglify.spec.js b/tests/uglify.spec.js index 5cbac8a..bc29b3c 100644 --- a/tests/uglify.spec.js +++ b/tests/uglify.spec.js @@ -19,9 +19,3 @@ test('Handles when out of ordinary phone numbers are thrown at it', t => { t.same(uglify('046 123 45 67 89'), '046123456789') t.end() }) - -// test('Uglify placeholders', t => { -// console.log(uglify('__________')) -// t.same(uglify('__________'), '') -// t.end() -// }) From 19c4586ea1d94710d89ac9e03d2cf5fdc899f8fc Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:23:04 -0400 Subject: [PATCH 08/27] Added: Type definitions for new functions --- types/index.d.cts | 16 ++++++++++++++++ types/index.d.ts | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/types/index.d.cts b/types/index.d.cts index 0c43194..b4033c8 100644 --- a/types/index.d.cts +++ b/types/index.d.cts @@ -29,10 +29,26 @@ declare namespace phoneFns { */ isValid(phone: string): boolean; + /** + * Validates a phone number based on a custom format provided + */ + isValidWithFormat(layout: string, phone: string): boolean; + isValidWithFormat(layout: string): (phone: string) => boolean; + + /** + * Strips all of the special characters from the given string but leaves extension and country code characters in place + */ + normalize(phone: string): string; + /** * Strips all of the special characters from the given string */ uglify(phone: string | number): string; + + /** + * Validates the base number, strips out special characters and spaces upon validation, can handle country code and extension in the phone number + */ + validate(phone: string): boolean; } } diff --git a/types/index.d.ts b/types/index.d.ts index 0c43194..b4033c8 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -29,10 +29,26 @@ declare namespace phoneFns { */ isValid(phone: string): boolean; + /** + * Validates a phone number based on a custom format provided + */ + isValidWithFormat(layout: string, phone: string): boolean; + isValidWithFormat(layout: string): (phone: string) => boolean; + + /** + * Strips all of the special characters from the given string but leaves extension and country code characters in place + */ + normalize(phone: string): string; + /** * Strips all of the special characters from the given string */ uglify(phone: string | number): string; + + /** + * Validates the base number, strips out special characters and spaces upon validation, can handle country code and extension in the phone number + */ + validate(phone: string): boolean; } } From b28807e6f19f29c1d679d7b5af67ec813ffa0818 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:23:55 -0400 Subject: [PATCH 09/27] Fixed: Param naming --- types/index.d.cts | 4 ++-- types/index.d.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/types/index.d.cts b/types/index.d.cts index b4033c8..41ea7df 100644 --- a/types/index.d.cts +++ b/types/index.d.cts @@ -32,8 +32,8 @@ declare namespace phoneFns { /** * Validates a phone number based on a custom format provided */ - isValidWithFormat(layout: string, phone: string): boolean; - isValidWithFormat(layout: string): (phone: string) => boolean; + isValidWithFormat(format: string, phone: string): boolean; + isValidWithFormat(format: string): (phone: string) => boolean; /** * Strips all of the special characters from the given string but leaves extension and country code characters in place diff --git a/types/index.d.ts b/types/index.d.ts index b4033c8..41ea7df 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -32,8 +32,8 @@ declare namespace phoneFns { /** * Validates a phone number based on a custom format provided */ - isValidWithFormat(layout: string, phone: string): boolean; - isValidWithFormat(layout: string): (phone: string) => boolean; + isValidWithFormat(format: string, phone: string): boolean; + isValidWithFormat(format: string): (phone: string) => boolean; /** * Strips all of the special characters from the given string but leaves extension and country code characters in place From d653550dddf80b5f95911ec585ba2ecd9a864bac Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:48:38 -0400 Subject: [PATCH 10/27] Added: findSeparators function --- src/findSeparators.js | 30 ++++++++++++++++++++++++++++++ tests/findSeparators.spec.js | 31 +++++++++++++++++++++++++++++++ types/index.d.cts | 5 +++++ types/index.d.ts | 5 +++++ 4 files changed, 71 insertions(+) create mode 100644 src/findSeparators.js create mode 100644 tests/findSeparators.spec.js diff --git a/src/findSeparators.js b/src/findSeparators.js new file mode 100644 index 0000000..f9c671b --- /dev/null +++ b/src/findSeparators.js @@ -0,0 +1,30 @@ +/** + * @name findSeparators + * @since v4.1.0 + * @function + * @category Function + * @sig String -> Array + * @description + * Finds a list of separators in a phone number string + * @param {String} phone The phone number to breakdown + * @return {Array} Returns an array of separators found in the phone number + * @example + * import { findSeparators } from 'phone-fns' + * + * findSeparators('123-456-7890') // => ['-'] + * findSeparators('123.456.7890') // => ['.'] + * findSeparators('123 456 7890') // => [' '] + * findSeparators('1234567890') // => [] + */ +function findSeparators (phone) { + const separators = ['-', '.', ' '] + const foundSeparators = [] + for (const separator of separators) { + if (phone.includes(separator)) { + foundSeparators.push(separator) + } + } + return foundSeparators +} + +export default findSeparators diff --git a/tests/findSeparators.spec.js b/tests/findSeparators.spec.js new file mode 100644 index 0000000..391155b --- /dev/null +++ b/tests/findSeparators.spec.js @@ -0,0 +1,31 @@ +import test from 'tape' +import findSeparators from '../src/findSeparators.js' // Adjust the path as necessary + +test('findSeparators - single separator', t => { + t.same(findSeparators('123-456-7890'), ['-'], 'Should return ["-"] for "123-456-7890"') + t.same(findSeparators('123.456.7890'), ['.'], 'Should return ["."] for "123.456.7890"') + t.same(findSeparators('123 456 7890'), [' '], 'Should return [" "] for "123 456 7890"') + t.end() +}) + +test('findSeparators - multiple separators', t => { + t.same(findSeparators('123-456 7890'), ['-', ' '], 'Should return ["-", " "] for "123-456 7890"') + t.same(findSeparators('123.456 7890'), ['.', ' '], 'Should return [".", " "] for "123.456 7890"') + t.same(findSeparators('123-456.7890'), ['-', '.'], 'Should return ["-", "."] for "123-456.7890"') + t.end() +}) + +test('findSeparators - no separators', t => { + t.same(findSeparators('1234567890'), [], 'Should return [] for "1234567890"') + t.end() +}) + +test('findSeparators - all separators', t => { + t.same(findSeparators('123-456.789 0'), ['-', '.', ' '], 'Should return ["-", ".", " "] for "123-456.789 0"') + t.end() +}) + +test('findSeparators - empty string', t => { + t.same(findSeparators(''), [], 'Should return [] for an empty string') + t.end() +}) diff --git a/types/index.d.cts b/types/index.d.cts index 41ea7df..e51c2c2 100644 --- a/types/index.d.cts +++ b/types/index.d.cts @@ -35,6 +35,11 @@ declare namespace phoneFns { isValidWithFormat(format: string, phone: string): boolean; isValidWithFormat(format: string): (phone: string) => boolean; + /** + * Finds a list of separators in a phone number string + */ + findSeparators(phone: string): string[]; + /** * Strips all of the special characters from the given string but leaves extension and country code characters in place */ diff --git a/types/index.d.ts b/types/index.d.ts index 41ea7df..5ffd5f3 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -24,6 +24,11 @@ declare namespace phoneFns { */ breakdown(phone: string): Breakdown; + /** + * Finds a list of separators in a phone number string + */ + findSeparators(phone: string): string[]; + /** * Validates the base number, does not take the country code or extension into consideration for this validation */ From a9044340f39aaa541325c027b045010ed4f961a0 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 03:55:02 -0400 Subject: [PATCH 11/27] Fixed: wording in docs --- src/isValidWithFormat.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isValidWithFormat.js b/src/isValidWithFormat.js index ddaa03a..c6fb91b 100644 --- a/src/isValidWithFormat.js +++ b/src/isValidWithFormat.js @@ -10,7 +10,7 @@ import validate from './validate.js' * @description * Validates a phone number based on a custom format provided * @param {String} format The format to validate against - * @param {String} phone The phone number to breakdown + * @param {String} phone The phone number to validate * @return {Boolean} Returns a boolean if the number provided is valid or not * @example * import { isValidWithFormat } from 'phone-fns' From 0a3b9d904c11279d67b5c58b4d6ea2b34ae8029b Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 09:14:25 -0400 Subject: [PATCH 12/27] Chore: cleanup types files --- types/index.d.cts | 42 +++++++++++++++++++++++++----------------- types/index.d.ts | 43 +++++++++++++++++++++++++------------------ 2 files changed, 50 insertions(+), 35 deletions(-) diff --git a/types/index.d.cts b/types/index.d.cts index e51c2c2..f8c0536 100644 --- a/types/index.d.cts +++ b/types/index.d.cts @@ -2,60 +2,68 @@ // Project: Phone-Fns // Definitions by: Dustin Hershman -declare let phoneFns: phoneFns.Static; +declare let phoneFns: phoneFns.Static declare namespace phoneFns { interface Breakdown { - areaCode: string; - localCode: string; - lineNumber: string; - extension: string; + countryCode?: string + areaCode: string + localCode: string + lineNumber: string + extension: string } interface Static { /** * Allows you to format phone numbers however you desire using N as number placeholders and C as country code placeholders these placeholders are case insensitive */ - format(layout: string, phone: string): string; - format(layout: string): (phone: string) => string; + format(layout: string, phone: string): string + format(layout: string): (phone: string) => string /** * Takes a provided phone string and breaks it down into an object of codes */ - breakdown(phone: string): Breakdown; + breakdown(phone: string): Breakdown + + /** + * Breaks down a phone number based on a custom format provided and returns an object with the parts of the phone number + * C - Country Code A- Area Code L - Local Code N - Line Number X - Extension + */ + breakdownWithFormat(format: string, phone: string): Breakdown + breakdownWithFormat(format: string): (phone: string) => Breakdown /** * Validates the base number, does not take the country code or extension into consideration for this validation */ - isValid(phone: string): boolean; + isValid(phone: string): boolean /** * Validates a phone number based on a custom format provided */ - isValidWithFormat(format: string, phone: string): boolean; - isValidWithFormat(format: string): (phone: string) => boolean; + isValidWithFormat(format: string, phone: string): boolean + isValidWithFormat(format: string): (phone: string) => boolean /** * Finds a list of separators in a phone number string */ - findSeparators(phone: string): string[]; + findSeparators(phone: string): string[] /** * Strips all of the special characters from the given string but leaves extension and country code characters in place */ - normalize(phone: string): string; + normalize(phone: string): string /** * Strips all of the special characters from the given string */ - uglify(phone: string | number): string; + uglify(phone: string | number): string /** * Validates the base number, strips out special characters and spaces upon validation, can handle country code and extension in the phone number */ - validate(phone: string): boolean; + validate(phone: string): boolean } } -export = phoneFns; -export as namespace phoneFns; +export = phoneFns +export as namespace phoneFns diff --git a/types/index.d.ts b/types/index.d.ts index 5ffd5f3..7d66433 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -1,61 +1,68 @@ // Type definitions for Phone-Fns // Project: Phone-Fns // Definitions by: Dustin Hershman - -declare let phoneFns: phoneFns.Static; +declare let phoneFns: phoneFns.Static declare namespace phoneFns { interface Breakdown { - areaCode: string; - localCode: string; - lineNumber: string; - extension: string; + countryCode?: string + areaCode: string + localCode: string + lineNumber: string + extension: string } interface Static { /** * Allows you to format phone numbers however you desire using N as number placeholders and C as country code placeholders these placeholders are case insensitive */ - format(layout: string, phone: string): string; - format(layout: string): (phone: string) => string; + format(layout: string, phone: string): string + format(layout: string): (phone: string) => string /** * Takes a provided phone string and breaks it down into an object of codes */ - breakdown(phone: string): Breakdown; + breakdown(phone: string): Breakdown + + /** + * Breaks down a phone number based on a custom format provided and returns an object with the parts of the phone number + * C - Country Code A- Area Code L - Local Code N - Line Number X - Extension + */ + breakdownWithFormat(format: string, phone: string): Breakdown + breakdownWithFormat(format: string): (phone: string) => Breakdown /** * Finds a list of separators in a phone number string */ - findSeparators(phone: string): string[]; + findSeparators(phone: string): string[] /** * Validates the base number, does not take the country code or extension into consideration for this validation */ - isValid(phone: string): boolean; + isValid(phone: string): boolean /** * Validates a phone number based on a custom format provided */ - isValidWithFormat(format: string, phone: string): boolean; - isValidWithFormat(format: string): (phone: string) => boolean; + isValidWithFormat(format: string, phone: string): boolean + isValidWithFormat(format: string): (phone: string) => boolean /** * Strips all of the special characters from the given string but leaves extension and country code characters in place */ - normalize(phone: string): string; + normalize(phone: string): string /** * Strips all of the special characters from the given string */ - uglify(phone: string | number): string; + uglify(phone: string | number): string /** * Validates the base number, strips out special characters and spaces upon validation, can handle country code and extension in the phone number */ - validate(phone: string): boolean; + validate(phone: string): boolean } } -export = phoneFns; -export as namespace phoneFns; +export = phoneFns +export as namespace phoneFns From b4a2ce019e06d6c042bbb4fc582238e0cf6322d5 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 09:46:01 -0400 Subject: [PATCH 13/27] Added: Curried test and error test to isValidWithFormat --- tests/isValidWithFormat.spec.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/isValidWithFormat.spec.js b/tests/isValidWithFormat.spec.js index c220a6f..0e7d652 100644 --- a/tests/isValidWithFormat.spec.js +++ b/tests/isValidWithFormat.spec.js @@ -18,3 +18,15 @@ test('isValidWithFormat with country code and extension', (t) => { t.same(isValidWithFormat('+1 NNN-NNN-NNNN x NNN', '+1 234-567-1890 x 123'), true, 'Valid phone number with country code and extension') t.end() }) + +test('isValidWithFormat curried', (t) => { + const fn = isValidWithFormat('NNN-NNN-NNNN') + t.same(fn('123-456-7890'), true, 'Valid phone number with curried function') + t.same(fn('1234567890'), false, 'Invalid phone number with curried function') + t.end() +}) + +test('isValidWithFormat - missing format', (t) => { + t.throws(() => isValidWithFormat(null, '123-456-7890'), /You must provide a format to validate/, 'Should throw an error if format is missing') + t.end() +}) From c289640418e59f10bd04463cc14773c01213f5ae Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 09:46:20 -0400 Subject: [PATCH 14/27] Added: error when format is not provided to isValidWithFormat --- src/isValidWithFormat.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/isValidWithFormat.js b/src/isValidWithFormat.js index c6fb91b..9fe495d 100644 --- a/src/isValidWithFormat.js +++ b/src/isValidWithFormat.js @@ -18,12 +18,16 @@ import validate from './validate.js' * isValidWithFormat('NNN-NNN-NNNN', '123-456-7890') // => true * isValidWithFormat('NNN-NNN-NNNN', '010-XYZ-1234') // => false * - * // its also curried + * // It's also curried * const fn = isValidWithFormat('NNN-NNN-NNNN') * fn('123-456-7890') // => true * fn('010-XYZ-1234') // => false */ function isValidWithFormat (format, phone) { + if (!format) { + throw new Error('You must provide a format to validate') + } + if (phone.length !== format.length) { return false } From 9037993b02ffe7227748626f9f198fa3d3d807ae Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 10:00:21 -0400 Subject: [PATCH 15/27] Updated: isValid Description --- src/isValid.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/isValid.js b/src/isValid.js index b76001c..cf7794a 100644 --- a/src/isValid.js +++ b/src/isValid.js @@ -38,7 +38,8 @@ function longNumberTest (phone) { * @category Function * @sig String -> Boolean * @description - * Validates the base number, does not take the country code or extension into consideration for this validation + * Validates the base number, does not take the country code or extension into consideration for this validation. + * Focuses more on NANP numbers and their format * @param {String} phone The phone number to breakdown * @return {Boolean} Returns a boolean if the number provided is valid or not * @example From f6bb2c1e9551922b67fda249f3b2ce1e35ae3ee5 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 10:03:06 -0400 Subject: [PATCH 16/27] Update: Unit test description for format --- tests/format.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/format.spec.js b/tests/format.spec.js index ac8d40b..21a2cdc 100644 --- a/tests/format.spec.js +++ b/tests/format.spec.js @@ -100,7 +100,7 @@ test('Catches letters when passed in', t => { t.end() }) -test('Handles unordinary phone numbers', t => { +test('Handles non NANP formats', t => { const results = format('NNN NNN NN NN NN', '046123456789') t.same(results, '046 123 45 67 89') From bad9cc924464dcd8a21a0e06a580921327044bc4 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 10:15:57 -0400 Subject: [PATCH 17/27] Fix: Keep tests consistent --- tests/normalize.spec.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/normalize.spec.js b/tests/normalize.spec.js index 01aff33..e0cfca3 100644 --- a/tests/normalize.spec.js +++ b/tests/normalize.spec.js @@ -2,28 +2,28 @@ import test from 'tape' import normalize from '../src/normalize.js' test('normalize removes spaces, dots, dashes, and parentheses', (t) => { - t.equal(normalize('123 456 7890'), '1234567890', 'should remove spaces') - t.equal(normalize('123.456.7890'), '1234567890', 'should remove dots') - t.equal(normalize('123-456-7890'), '1234567890', 'should remove dashes') - t.equal(normalize('(123) 456-7890'), '1234567890', 'should remove parentheses') - t.equal(normalize('(123) 456-7890 x123'), '1234567890x123', 'should handle extension') - t.equal(normalize('(123) 456-7890 x 123'), '1234567890x123', 'should handle extension') + t.same(normalize('123 456 7890'), '1234567890', 'should remove spaces') + t.same(normalize('123.456.7890'), '1234567890', 'should remove dots') + t.same(normalize('123-456-7890'), '1234567890', 'should remove dashes') + t.same(normalize('(123) 456-7890'), '1234567890', 'should remove parentheses') + t.same(normalize('(123) 456-7890 x123'), '1234567890x123', 'should handle extension') + t.same(normalize('(123) 456-7890 x 123'), '1234567890x123', 'should handle extension') t.end() }) test('normalize trims the input', (t) => { - t.equal(normalize(' 1234567890 '), '1234567890', 'should trim leading and trailing spaces') - t.equal(normalize('\t1234567890\t'), '1234567890', 'should trim leading and trailing tabs') + t.same(normalize(' 1234567890 '), '1234567890', 'should trim leading and trailing spaces') + t.same(normalize('\t1234567890\t'), '1234567890', 'should trim leading and trailing tabs') t.end() }) test('normalize handles mixed characters', (t) => { - t.equal(normalize(' (123) 456-7890. '), '1234567890', 'should remove mixed characters and trim') + t.same(normalize(' (123) 456-7890. '), '1234567890', 'should remove mixed characters and trim') t.end() }) test('normalize handles empty and null input', (t) => { - t.equal(normalize(''), '', 'should handle empty string') - t.equal(normalize(null), '', 'should handle null input') + t.same(normalize(''), '', 'should handle empty string') + t.same(normalize(null), '', 'should handle null input') t.end() }) From bdcca8599d991fffaf4cce1a135439354e76e350 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 10:34:45 -0400 Subject: [PATCH 18/27] Added: breakdownWithFormat function --- src/breakdownWithFormat.js | 67 +++++++++++++++++++++++++++++++ tests/breakdownWithFormat.spec.js | 65 ++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 src/breakdownWithFormat.js create mode 100644 tests/breakdownWithFormat.spec.js diff --git a/src/breakdownWithFormat.js b/src/breakdownWithFormat.js new file mode 100644 index 0000000..dccade8 --- /dev/null +++ b/src/breakdownWithFormat.js @@ -0,0 +1,67 @@ +import _curry2 from './_internals/_curry2.js' +import isValidWithFormat from './isValidWithFormat.js' + +/** + * @name breakdownWithFormat + * @since v4.1.0 + * @function + * @category Function + * @sig String -> String -> Object + * @description + * Breaks down a phone number based on a custom format provided and returns an object with the parts of the phone number + * C - Country Code A- Area Code L - Local Code N - Line Number X - Extension + * Does NOT work with placeholders + * @param {String} format The format to validate against + * @param {String} phone The phone number to breakdown + * @return {Object} Returns an object with the parts of the phone number + * @example + * import { breakdownWithFormat } from 'phone-fns' + * + * breakdownWithFormat('+C (AAA) LLL-NNNN xXXX', '+1-555-444-3333 x123') // => { countryCode: '1', areaCode: '555', localCode: '444', lineNumber: '3333', extension: '123' } + * breakdownWithFormat('AAA-LLL-NNNN', '010-XYZ-1234') // => Error: The phone number provided does not match the format provided or is an invalid phone number + * + * // it's also curried + * const fn = breakdownWithFormat('+C (AAA) LLL-NNNN xXXX') + * fn('+1-555-444-3333 x123') // => { countryCode: '', areaCode: '123', localCode: '456', lineNumber: '7890', extension: '' } + */ +function breakdownWithFormat (format, phone) { + if (!format) { + throw new Error('You must provide a format to breakdown') + } + + if (!isValidWithFormat(format, phone)) { + throw new Error('The phone number provided does not match the format provided or is an invalid phone number') + } + + const results = { + countryCode: '', + areaCode: '', + localCode: '', + lineNumber: '', + extension: '' + } + + for (let i = 0; i < format.length; i++) { + switch (format[i]) { + case 'C': + results.countryCode += phone[i] + break + case 'A': + results.areaCode += phone[i] + break + case 'N': + results.lineNumber += phone[i] + break + case 'L': + results.localCode += phone[i] + break + case 'X': + results.extension += phone[i] + break + } + } + + return results +} + +export default _curry2(breakdownWithFormat) diff --git a/tests/breakdownWithFormat.spec.js b/tests/breakdownWithFormat.spec.js new file mode 100644 index 0000000..1658597 --- /dev/null +++ b/tests/breakdownWithFormat.spec.js @@ -0,0 +1,65 @@ +import test from 'tape' +import breakdownWithFormat from '../src/breakdownWithFormat.js' + +test('breakdownWithFormat - valid input', (t) => { + const format = '+C (AAA) LLL-NNNN xXXX' + const phone = '+1 (555) 444-3333 x123' + const expected = { + countryCode: '1', + areaCode: '555', + localCode: '444', + lineNumber: '3333', + extension: '123' + } + + const result = breakdownWithFormat(format, phone) + t.deepEqual(result, expected, 'Should correctly breakdown the phone number with the given format') + t.end() +}) + +test('breakdownWithFormat - missing format', (t) => { + const phone = '+1 (555) 444-3333 x123' + + t.throws(() => breakdownWithFormat(null, phone), /You must provide a format to breakdown/, 'Should throw an error if format is missing') + t.end() +}) + +test('breakdownWithFormat - invalid phone number', (t) => { + const format = '+C (AAA) LLL-NNNN xXXX' + const phone = 'invalid phone number' + + t.throws(() => breakdownWithFormat(format, phone), /The phone number provided does not match the format provided or is an invalid phone number/, 'Should throw an error if phone number does not match the format') + t.end() +}) + +test('breakdownWithFormat - different format', (t) => { + const format = '+C-AAA-LLL-NNNN xXXX' + const phone = '+1-555-444-3333 x123' + const expected = { + countryCode: '1', + areaCode: '555', + localCode: '444', + lineNumber: '3333', + extension: '123' + } + + const result = breakdownWithFormat(format, phone) + t.deepEqual(result, expected, 'Should correctly breakdown the phone number with a different format') + t.end() +}) + +test('breakdownWithFormat - no extension', (t) => { + const format = '+C (AAA) LLL-NNNN' + const phone = '+1 (555) 444-3333' + const expected = { + countryCode: '1', + areaCode: '555', + localCode: '444', + lineNumber: '3333', + extension: '' + } + + const result = breakdownWithFormat(format, phone) + t.deepEqual(result, expected, 'Should correctly breakdown the phone number without an extension') + t.end() +}) From 52c1bd5cb920a3d4ed77fc3293605ad1708b7bfa Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 10:38:34 -0400 Subject: [PATCH 19/27] Fixed: Standarize testing --- tests/breakdownWithFormat.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/breakdownWithFormat.spec.js b/tests/breakdownWithFormat.spec.js index 1658597..9c6f1b3 100644 --- a/tests/breakdownWithFormat.spec.js +++ b/tests/breakdownWithFormat.spec.js @@ -13,7 +13,7 @@ test('breakdownWithFormat - valid input', (t) => { } const result = breakdownWithFormat(format, phone) - t.deepEqual(result, expected, 'Should correctly breakdown the phone number with the given format') + t.same(result, expected, 'Should correctly breakdown the phone number with the given format') t.end() }) @@ -44,7 +44,7 @@ test('breakdownWithFormat - different format', (t) => { } const result = breakdownWithFormat(format, phone) - t.deepEqual(result, expected, 'Should correctly breakdown the phone number with a different format') + t.same(result, expected, 'Should correctly breakdown the phone number with a different format') t.end() }) @@ -60,6 +60,6 @@ test('breakdownWithFormat - no extension', (t) => { } const result = breakdownWithFormat(format, phone) - t.deepEqual(result, expected, 'Should correctly breakdown the phone number without an extension') + t.same(result, expected, 'Should correctly breakdown the phone number without an extension') t.end() }) From fdb6812f4876fc4e1e45118c7b462a8f77cf5c71 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:28:44 -0400 Subject: [PATCH 20/27] Updated: Strip out kyanite from isValid --- src/isValid.js | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/isValid.js b/src/isValid.js index cf7794a..97dfb39 100644 --- a/src/isValid.js +++ b/src/isValid.js @@ -1,8 +1,16 @@ -import { compose, when, F, reduced, eq, isEmpty, length, lt, pipe, test } from 'kyanite' - import breakdown from './breakdown.js' import uglify from './uglify.js' +/** + * @private + * @function + * @param {String} x The value to check if it is empty + * @returns {Boolean} Whether or not the value is empty + */ +function isEmpty (x) { + return x === '' +} + /** * @private * @function @@ -10,11 +18,10 @@ import uglify from './uglify.js' * @return {Boolean} Whether or not the phone passed validation */ function shortNumberTest (phone) { - return () => { - const { localCode, lineNumber } = breakdown(phone) + const { localCode, lineNumber } = breakdown(phone) + const str = localCode + lineNumber - return test(/^([0-9]{3})[-. ]?([0-9]{4})$/, localCode + lineNumber) - } + return /^([0-9]{3})[-. ]?([0-9]{4})$/.test(str) } /** @@ -24,11 +31,10 @@ function shortNumberTest (phone) { * @return {Boolean} Whether or not the phone passed validation */ function longNumberTest (phone) { - return () => { - const { areaCode, localCode, lineNumber } = breakdown(phone) + const { areaCode, localCode, lineNumber } = breakdown(phone) + const str = areaCode + localCode + lineNumber - return test(/^\+?([0-9]{2})\)?[-. ]?([0-9]{4})[-. ]?([0-9]{4})$/, areaCode + localCode + lineNumber) - } + return /^\+?([0-9]{2})\)?[-. ]?([0-9]{4})[-. ]?([0-9]{4})$/.test(str) } /** @@ -48,13 +54,15 @@ function longNumberTest (phone) { */ export default function isValid (phone) { const uglyPhone = uglify(phone) - const done = compose(reduced) - - return pipe([ - when(isEmpty, done(F)), - length, - when(lt(7), done(F)), - when(eq(7), shortNumberTest(uglyPhone)), - longNumberTest(uglyPhone) - ], uglyPhone) + const len = uglyPhone.length + + if (isEmpty(uglyPhone) || len < 7) { + return false + } + + if (len === 7) { + return shortNumberTest(uglyPhone) + } + + return longNumberTest(uglyPhone) } From 5e85d46dacd29d4d45f17d3e02cc66ffd0e73456 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:28:52 -0400 Subject: [PATCH 21/27] Updated: Strip out kyanite from uglify --- src/uglify.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uglify.js b/src/uglify.js index f1f2297..5956f1b 100644 --- a/src/uglify.js +++ b/src/uglify.js @@ -1,5 +1,3 @@ -import { replace, compose } from 'kyanite' - /** * @name uglify * @since v0.1.0 @@ -14,6 +12,8 @@ import { replace, compose } from 'kyanite' * uglify('555-444-3333') // => '5554443333' * uglify('5554443333') // => '5554443333' */ -const uglify = compose(replace(/[a-z]\w?|\W/gi, ''), String) +function uglify (phone) { + return String(phone).replace(/[a-z]\w?|\W/gi, '') +} export default uglify From 1a6c2208bcfb2e96df34a2980bd2d7491a9a4d7c Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:28:57 -0400 Subject: [PATCH 22/27] Updated: Strip out kyanite from format --- src/format.js | 74 +++++++++++++++++++++------------------------------ 1 file changed, 30 insertions(+), 44 deletions(-) diff --git a/src/format.js b/src/format.js index 6c5b760..14a68de 100644 --- a/src/format.js +++ b/src/format.js @@ -1,25 +1,8 @@ -import { - add, - addIndex, - both, - branch, - complement, - compose, - countBy, - eq, - gt, - identity, - includes, - length, - pipe, - reduce, - replace, - split, - toUpper -} from 'kyanite' import _curry2 from './_internals/_curry2.js' import isValid from './isValid.js' import uglify from './uglify.js' +import _uglifyFormats from './_internals/_uglifyFormats.js' +import _hasPlaceholder from './_internals/_hasPlaceholder.js' /** * @private @@ -27,16 +10,8 @@ import uglify from './uglify.js' * @param {String} layout The desired layout format * @param {String} phone The phone number to validate against */ -function validFormat (layout) { - return phone => { - const { N, C = 0 } = compose(countBy(toUpper), split(''), layout) - - return pipe([ - uglify, - length, - eq(add(N, C)) - ], phone) - } +function validFormat (layout, phone) { + return phone.length === _uglifyFormats(layout).length } /** @@ -66,22 +41,33 @@ function validFormat (layout) { * fn('(333) 444-5555') // => '333.444.5555' */ function format (layout, phone) { - const cCount = includes('C', layout) ? length(layout.match(/C/g)) : 0 - const _reduce = addIndex(reduce) + let cCount = 0 + const uglyPhone = uglify(phone) + + if (layout.includes('C')) { + cCount = (layout.match(/C/g) || []).length + } + + if (!validFormat(layout, uglyPhone)) { + return phone + } + + if (!_hasPlaceholder(uglyPhone)) { + // We are skipping validation if there are placeholders + if (!isValid(phone)) { + return phone + } + } + + return uglify(uglyPhone).split('').reduce((acc, d, i) => { + if (cCount > i) { + acc = acc.replace(/C/i, d) + } else { + acc = acc.replace(/N/i, d) + } - return branch( - both( - complement(isValid), - complement(validFormat(layout)) - ), - identity, - pipe([ - uglify, - split(''), - _reduce((d, acc, i) => gt(i, cCount) ? replace(/C/i, d, acc) : replace(/N/i, d, acc), layout) - ]), - phone - ) + return acc + }, layout) } export default _curry2(format) From 1f4ec8fff8c6a86b1ebe2031342e1ae869dc6548 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:29:49 -0400 Subject: [PATCH 23/27] Added: New internals for format to use --- src/_internals/_hasPlaceholder.js | 9 +++++++++ src/_internals/_uglifyFormats.js | 9 +++++++++ 2 files changed, 18 insertions(+) create mode 100644 src/_internals/_hasPlaceholder.js create mode 100644 src/_internals/_uglifyFormats.js diff --git a/src/_internals/_hasPlaceholder.js b/src/_internals/_hasPlaceholder.js new file mode 100644 index 0000000..6f991b6 --- /dev/null +++ b/src/_internals/_hasPlaceholder.js @@ -0,0 +1,9 @@ +/** + * @private + * @function + * @param {String} str The string to check for placeholders + * @returns {Boolean} Whether or not the string has a placeholder + */ +export default function _hasPlaceholder (str) { + return str.includes('_') +} diff --git a/src/_internals/_uglifyFormats.js b/src/_internals/_uglifyFormats.js new file mode 100644 index 0000000..eb8fef1 --- /dev/null +++ b/src/_internals/_uglifyFormats.js @@ -0,0 +1,9 @@ +/** + * @private + * @function + * @param {String} str The string to strip special characters + * @returns {String} The newly created string with special characters stripped + */ +export default function _uglifyFormats (str) { + return str.replace(/[^a-wyz]/gi, '') +} From 7be22d6d159993abab7ae4dc7e588042b8884f83 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:32:03 -0400 Subject: [PATCH 24/27] Updated: Dependency Updates and removed Kyanite --- package-lock.json | 75 ++++++++++++++++++++--------------------------- package.json | 14 ++++----- 2 files changed, 37 insertions(+), 52 deletions(-) diff --git a/package-lock.json b/package-lock.json index cfe9a0e..07d2947 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,13 @@ { "name": "phone-fns", - "version": "4.0.2", + "version": "4.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "phone-fns", - "version": "4.0.2", + "version": "4.1.0", "license": "MIT", - "dependencies": { - "kyanite": "3.1.0" - }, "devDependencies": { "@babel/core": "7.25.7", "@babel/preset-env": "7.25.7", @@ -20,7 +17,7 @@ "globby": "13.2.2", "jsdoc": "4.0.3", "npm-run-all": "4.1.5", - "pinet": "1.1.5", + "pinet": "1.2.0", "rollup": "4.24.0", "rollup-plugin-filesize": "10.0.0", "standard": "17.1.2", @@ -5356,9 +5353,9 @@ } }, "node_modules/highlight.js": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", - "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", + "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", "dev": true, "engines": { "node": ">=12.0.0" @@ -6161,7 +6158,8 @@ "node_modules/kyanite": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/kyanite/-/kyanite-3.1.0.tgz", - "integrity": "sha512-5jXNGnnXxcHNbWgHjTD5/paRTp0Em4C6hU+jHNSGDtY7seeDlLJey480jpLIxkU2LbiZNY0QwS22TdqRTSNY9A==" + "integrity": "sha512-5jXNGnnXxcHNbWgHjTD5/paRTp0Em4C6hU+jHNSGDtY7seeDlLJey480jpLIxkU2LbiZNY0QwS22TdqRTSNY9A==", + "dev": true }, "node_modules/levn": { "version": "0.4.1", @@ -7417,27 +7415,21 @@ } }, "node_modules/pinet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pinet/-/pinet-1.1.5.tgz", - "integrity": "sha512-MtKVa/EZalAWq6xSDCVfXsczaKmz1zrI7zl3u2FhiT+5SyuEyw+XCOjnWdKnFxl2MfpT4pW005Dadbri0P8Hgw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pinet/-/pinet-1.2.0.tgz", + "integrity": "sha512-DKBh1ozkqgUpC1hoHLkKSDOgkB+rLF5IQ9sFLszMXmGAvbmvpLULG/CDdqhhHAn8pxFOWSN9ql/7f6QsTVdGug==", "dev": true, "dependencies": { "fs-extra": "11.2.0", - "highlight.js": "11.9.0", - "kyanite": "2.0.1", - "marked": "11.2.0" + "highlight.js": "11.10.0", + "kyanite": "3.1.0", + "marked": "14.1.2" } }, - "node_modules/pinet/node_modules/kyanite": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kyanite/-/kyanite-2.0.1.tgz", - "integrity": "sha512-Tr3pRIVvIfQl6DPrkBPV9PMditHLKhpTEAJYwq1p+/W/YpHGQ9E/jAW+tvW+h+ekoV+5xOkicdL1eYjDje0NyQ==", - "dev": true - }, "node_modules/pinet/node_modules/marked": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", - "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.2.tgz", + "integrity": "sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==", "dev": true, "bin": { "marked": "bin/marked.js" @@ -13382,9 +13374,9 @@ } }, "highlight.js": { - "version": "11.9.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.9.0.tgz", - "integrity": "sha512-fJ7cW7fQGCYAkgv4CPfwFHrfd/cLS4Hau96JuJ+ZTOWhjnhoeN1ub1tFmALm/+lW5z4WCAuAV9bm05AP0mS6Gw==", + "version": "11.10.0", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.10.0.tgz", + "integrity": "sha512-SYVnVFswQER+zu1laSya563s+F8VDGt7o35d4utbamowvUNLLMovFqwCLSocpZTz3MgaSRA1IbqRWZv97dtErQ==", "dev": true }, "hosted-git-info": { @@ -13959,7 +13951,8 @@ "kyanite": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/kyanite/-/kyanite-3.1.0.tgz", - "integrity": "sha512-5jXNGnnXxcHNbWgHjTD5/paRTp0Em4C6hU+jHNSGDtY7seeDlLJey480jpLIxkU2LbiZNY0QwS22TdqRTSNY9A==" + "integrity": "sha512-5jXNGnnXxcHNbWgHjTD5/paRTp0Em4C6hU+jHNSGDtY7seeDlLJey480jpLIxkU2LbiZNY0QwS22TdqRTSNY9A==", + "dev": true }, "levn": { "version": "0.4.1", @@ -14926,27 +14919,21 @@ "dev": true }, "pinet": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/pinet/-/pinet-1.1.5.tgz", - "integrity": "sha512-MtKVa/EZalAWq6xSDCVfXsczaKmz1zrI7zl3u2FhiT+5SyuEyw+XCOjnWdKnFxl2MfpT4pW005Dadbri0P8Hgw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pinet/-/pinet-1.2.0.tgz", + "integrity": "sha512-DKBh1ozkqgUpC1hoHLkKSDOgkB+rLF5IQ9sFLszMXmGAvbmvpLULG/CDdqhhHAn8pxFOWSN9ql/7f6QsTVdGug==", "dev": true, "requires": { "fs-extra": "11.2.0", - "highlight.js": "11.9.0", - "kyanite": "2.0.1", - "marked": "11.2.0" + "highlight.js": "11.10.0", + "kyanite": "3.1.0", + "marked": "14.1.2" }, "dependencies": { - "kyanite": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/kyanite/-/kyanite-2.0.1.tgz", - "integrity": "sha512-Tr3pRIVvIfQl6DPrkBPV9PMditHLKhpTEAJYwq1p+/W/YpHGQ9E/jAW+tvW+h+ekoV+5xOkicdL1eYjDje0NyQ==", - "dev": true - }, "marked": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/marked/-/marked-11.2.0.tgz", - "integrity": "sha512-HR0m3bvu0jAPYiIvLUUQtdg1g6D247//lvcekpHO1WMvbwDlwSkZAX9Lw4F4YHE1T0HaaNve0tuAWuV1UJ6vtw==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-14.1.2.tgz", + "integrity": "sha512-f3r0yqpz31VXiDB/wj9GaOB0a2PRLQl6vJmXiFrniNwjkKdvakqJRULhjFKJpxOchlCRiG5fcacoUZY5Xa6PEQ==", "dev": true } } diff --git a/package.json b/package.json index 9881b14..c7d9e8a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "phone-fns", - "version": "4.0.2", + "version": "4.1.0", "description": "A small, modern, and functional phone library for javascript", "main": "dist/phone-fns.min.js", "module": "src/index.js", @@ -11,10 +11,10 @@ "scripts": { "prepack": "npm-run-all --parallel docs scripts lint test --serial build", "scripts": "node scripts/create-export.js", - "docs": "node_modules/.bin/jsdoc -c jsdoc.json", + "docs": "jsdoc -c jsdoc.json", "build": "rollup -c", "lint": "standard src/**/*.js", - "test": "tape tests/*.js | tap-on" + "test": "tape tests/format.spec.js | tap-on" }, "exports": { ".": { @@ -35,7 +35,8 @@ "standard": { "ignore": [ "docs/*", - "dist/*" + "dist/*", + "types/*" ] }, "repository": { @@ -71,14 +72,11 @@ "globby": "13.2.2", "jsdoc": "4.0.3", "npm-run-all": "4.1.5", - "pinet": "1.1.5", + "pinet": "1.2.0", "rollup": "4.24.0", "rollup-plugin-filesize": "10.0.0", "standard": "17.1.2", "tap-on": "1.0.0", "tape": "5.9.0" - }, - "dependencies": { - "kyanite": "3.1.0" } } From a6a1973c2deedbec11fa58a4e3bb05d0a9630ef1 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:37:18 -0400 Subject: [PATCH 25/27] Fixed: Make sure all tests are ran not just format --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c7d9e8a..661b495 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "docs": "jsdoc -c jsdoc.json", "build": "rollup -c", "lint": "standard src/**/*.js", - "test": "tape tests/format.spec.js | tap-on" + "test": "tape tests/*.spec.js | tap-on" }, "exports": { ".": { From 027d04ea19e056097d3b575668c2725a3e24e686 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:37:32 -0400 Subject: [PATCH 26/27] Fixed: Unneeded call to uglify --- src/format.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/format.js b/src/format.js index 14a68de..8fc598a 100644 --- a/src/format.js +++ b/src/format.js @@ -53,13 +53,13 @@ function format (layout, phone) { } if (!_hasPlaceholder(uglyPhone)) { - // We are skipping validation if there are placeholders + // We are skipping validation of the phone number if there are placeholders if (!isValid(phone)) { return phone } } - return uglify(uglyPhone).split('').reduce((acc, d, i) => { + return uglyPhone.split('').reduce((acc, d, i) => { if (cCount > i) { acc = acc.replace(/C/i, d) } else { From d06cd289564968e4c5ae237e307bb670c82d5252 Mon Sep 17 00:00:00 2001 From: dhershman1 Date: Sun, 13 Oct 2024 11:38:38 -0400 Subject: [PATCH 27/27] Update: Changelog --- changelog.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/changelog.md b/changelog.md index c65da4a..3b11747 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,36 @@ # Changelog +## v4.1.0 + +### New + +- Added `normalize` function + - This function strips out special characters and trims the phone number, much like uglify but skips non-digit characters + - Example: `normalize('555.444.3333 x 123') // => '5554443333x123'` vs `uglify('555.444.3333 x 123') // => 5554443333123` +- Added `validate` function + - This is a validation function, but works better for world wide phone numbers as well. Expects the full number + - Example: `333-444-5555` comes back valid but `444-5555` is invalid to this function +- Added `isValidWithFormat` function + - This takes a string phone number and a format string and validates the phone using the format + - It's also passed through the `validate` function for an extra step of validation +- Added `findSeparators` function + - A simple function that finds the separators in a phone number and returns them as an array +- Added `breakdownWithFormat` function + - Works a lot like `breakdown` but follows a strict format provided by the user to breakdown the number into an object + - This allows for a wider range of phone number support for breakdown + + +### Changed + +- `Phone-fns` is no longer dependant on `Kyanite` and is dependency free! +- `isValid` description to explain that it mostly focused on NANP numbers +- `breakdown` description to better explain that it's main focus is NANP numbers and its gachas +- We more than doubled our unit tests! Woo! + +### Chore + +- Renamed test files to `*.spec.js` instead of just `*.js` + ## v4.0.2 ### Fixed