Repository: validatorjs/validator.js

Issue:
`String.prototype.includes()` is not supported in IE
Is IE a supported browser? If so, https://github.com/validatorjs/validator.js/blob/master/validator.js#L429 has a call to `String.prototype.includes()`, which fails in IE as it is not available on that browser.

A polyfill is available at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes



Hints from issue comments:
@profnandaa: @lifenstein -- do you need to add the polyfill from your end? 🤔@mum-never-proud: @profnandaa I guess we have two options here,

either use a polyfill-package or write our own, I would rather suggest going with the later one

your thoughts?@profnandaa: @mum-never-proud -- How about just having a backward compatible `.include` equivalent, for that line instead? Good old `.indexOf`@mum-never-proud: yep works shall I raise a pr?

cc:  @profnandaa @profnandaa: For sure. Also scan to see if there’s another use of this or the newer
array/string methods anywhere else.

Thanks!

./na

On Mon, Mar 30, 2020 at 3:36 AM mum-never-proud <notifications@github.com>
wrote:

> yep works shall I raise a pr?
>
> cc: @profnandaa <https://github.com/profnandaa>
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/validatorjs/validator.js/issues/1272#issuecomment-605727696>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAB7ZEM726XP2QA3FNPTPZTRJ7SSTANCNFSM4LSNH74Q>
> .
>
-- 
Sent from a tiny device while on the move.
@mum-never-proud: @profnandaa I still think to install a polyfill rather than using `indexOf` (may lead to confusion) and isn't clear.

like there's a case in our code 

`!arr.includes(0)` -> `arr.indexOf(0) < 0` doesn't look much readable to me

in future some other function may not work in IE, either drop support for IE or include polyfill (prefer this)

moreover, it's always better to include a polyfill since we need to support all the browsers

`@babel/polyfill` can do but as of 7.4 it has been deprecated in favor of `core-js`

your thoughts?

cc: @profnandaa 
@lifenstein: > @lifenstein -- do you need to add the polyfill from your end?

I've used the polyfill from MDN for the time being, but it would be good to have it handled within validator. The IE (or browser support in general) isn't documented, so I was hoping to get that clarified as well, in case there are other places where it breaks. From the discussion, it seems all (atleast all popular-ish) browsers are expected to be supported, so perhaps adding browser testing is good as well.@profnandaa: May be users could include polyfill from their end? I feel doing so from our end introduces unnecessary overheads.

@lifenstein -- I agree on having some tests against minimum browser versions.@mum-never-proud: I really don't think its a good idea to expect a user to use a polyfill@profnandaa: What does creating our own polyfill entail? And how much will it add to the current footprint?@fpedroza: We were just bitten by this issue when we moved from v3.6 to v3.7, which was quite disappointing. I fully agree that some clarification of supported browsers should be added. @WikiRik: I've quickly made PR #2099 that could potentially fix this issue.@pano9000: IE11 is officially dead, so I personally would not remove support for any of the ES6 methods to support such an old (unsupported) browser.

Original issue is from 2020, since then, IE11 support has officially ended since June, and it seems there are plans to actively disable IE11 installation from MS in Feb '23.

So instead of replacing the newer, faster ES6 methods, we should rather look into adding support for this via Babel/Rollup (if at all)

some resources:
https://babeljs.io/docs/en/babel-preset-env

https://nolongerset.com/death-of-ie11/
https://death-to-ie11.com/
https://caniuse.com/es6-string-includes@fpedroza: Yes, IE11 is dead, but sadly it is still widely used in the medical field
since many hospital environments are so far behind the times.

On Sun, Dec 18, 2022, 3:33 PM Panagiotis Papadopoulos <
***@***.***> wrote:

> IE11 is officially dead, so I personally would not remove support for any
> of the ES6 methods to support such an old (unsupported) browser.
>
> Original issue is from 2020, since then, IE11 support has officially ended
> since June, and it seems there are plans to actively disable IE11
> installation from MS in Feb '23.
>
> So instead of replacing the newer, faster ES6 methods, we should rather
> look into adding support for this via Babel/Rollup (if at all)
>
> some resources:
> https://babeljs.io/docs/en/babel-preset-env
>
> https://nolongerset.com/death-of-ie11/
> https://death-to-ie11.com/
> https://caniuse.com/es6-string-includes
>
> —
> Reply to this email directly, view it on GitHub
> <https://github.com/validatorjs/validator.js/issues/1272#issuecomment-1356891076>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AA5GTM6TLOO6DUXIEHAOD4TWN6GM3ANCNFSM4LSNH74Q>
> .
> You are receiving this because you commented.Message ID:
> ***@***.***>
>


Solution patch:
diff --git a/README.md b/README.md
index a64ca45..e1661b5 100644
--- a/README.md
+++ b/README.md
@@ -157,7 +157,7 @@ Validator                               | Description
 **isOctal(str)**                        | check if the string is a valid octal number.
 **isPassportNumber(str, countryCode)**    | check if the string is a valid passport number.<br/><br/>`countryCode` is one of `['AM', 'AR', 'AT', 'AU', 'AZ', 'BE', 'BG', 'BY', 'BR', 'CA', 'CH', 'CN', 'CY', 'CZ', 'DE', 'DK', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HU', 'IE', 'IN', 'IR', 'ID', 'IS', 'IT', 'JM', 'JP', 'KR', 'KZ', 'LI', 'LT', 'LU', 'LV', 'LY', 'MT', 'MX', 'MY', 'MZ', 'NL', 'NZ', 'PH', 'PK', 'PL', 'PT', 'RO', 'RU', 'SE', 'SL', 'SK', 'TH', 'TR', 'UA', 'US', 'ZA']`.  Locale list is `validator.passportNumberLocales`.
 **isPort(str)**                         | check if the string is a valid port number.
-**isPostalCode(str, locale)**           | check if the string is a postal code.<br/><br/>`locale` is one of `['AD', 'AT', 'AU', 'AZ', 'BA', 'BE', 'BG', 'BR', 'BY', 'CA', 'CH', 'CN', 'CO', 'CZ', 'DE', 'DK', 'DO', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'KE', 'KR', 'LI', 'LK', 'LT', 'LU', 'LV', 'MG', 'MT', 'MX', 'MY', 'NL', 'NO', 'NP', 'NZ', 'PL', 'PR', 'PT', 'RO', 'RU', 'SA', 'SE', 'SG', 'SI', 'SK', 'TH', 'TN', 'TW', 'UA', 'US', 'ZA', 'ZM']` OR `'any'`. If 'any' is used, function will check if any of the locales match. Locale list is `validator.isPostalCodeLocales`.
+**isPostalCode(str, locale)**           | check if the string is a postal code.<br/><br/>`locale` is one of `['AD', 'AT', 'AU', 'AZ', 'BA', 'BE', 'BG', 'BR', 'BY', 'CA', 'CH', 'CN', 'CO', 'CZ', 'DE', 'DK', 'DO', 'DZ', 'EE', 'ES', 'FI', 'FR', 'GB', 'GR', 'HR', 'HT', 'HU', 'ID', 'IE', 'IL', 'IN', 'IR', 'IS', 'IT', 'JP', 'KE', 'KR', 'LI', 'LK', 'LT', 'LU', 'LV', 'MG', 'MT', 'MX', 'MY', 'NL', 'NO', 'NP', 'NZ', 'PK', 'PL', 'PR', 'PT', 'RO', 'RU', 'SA', 'SE', 'SG', 'SI', 'SK', 'TH', 'TN', 'TW', 'UA', 'US', 'ZA', 'ZM']` OR `'any'`. If 'any' is used, function will check if any of the locales match. Locale list is `validator.isPostalCodeLocales`.
 **isRFC3339(str)**                      | check if the string is a valid [RFC 3339][RFC 3339] date.
 **isRgbColor(str [,options])**                     | check if the string is a rgb or rgba color.<br/></br>`options` is an object with the following properties<br/><br/>`includePercentValues` defaults to `true`. If you don't want to allow to set `rgb` or `rgba` values with percents, like `rgb(5%,5%,5%)`, or `rgba(90%,90%,90%,.3)`, then set it to false.<br/><br/>`allowSpaces` defaults to `true`, which prohibits whitespace. If set to false, whitespace between color values is allowed, such as `rgb(255, 255, 255)` or even `rgba(255,       128,        0,      0.7)`.
 **isSemVer(str)**                       | check if the string is a Semantic Versioning Specification (SemVer).
diff --git a/src/lib/isBoolean.js b/src/lib/isBoolean.js
index 9fddc2b..40ebd35 100644
--- a/src/lib/isBoolean.js
+++ b/src/lib/isBoolean.js
@@ -1,4 +1,5 @@
 import assertString from './util/assertString';
+import includes from './util/includesArray';
 
 const defaultOptions = { loose: false };
 const strictBooleans = ['true', 'false', '1', '0'];
@@ -8,8 +9,8 @@ export default function isBoolean(str, options = defaultOptions) {
   assertString(str);
 
   if (options.loose) {
-    return looseBooleans.includes(str.toLowerCase());
+    return includes(looseBooleans, str.toLowerCase());
   }
 
-  return strictBooleans.includes(str);
+  return includes(strictBooleans, str);
 }
diff --git a/src/lib/isDecimal.js b/src/lib/isDecimal.js
index 488668a..474eff2 100644
--- a/src/lib/isDecimal.js
+++ b/src/lib/isDecimal.js
@@ -1,6 +1,6 @@
 import merge from './util/merge';
 import assertString from './util/assertString';
-import includes from './util/includes';
+import includes from './util/includesArray';
 import { decimal } from './alpha';
 
 function decimalRegExp(options) {
diff --git a/src/lib/isIBAN.js b/src/lib/isIBAN.js
index 94ac67c..ed0abd6 100644
--- a/src/lib/isIBAN.js
+++ b/src/lib/isIBAN.js
@@ -1,4 +1,5 @@
 import assertString from './util/assertString';
+import includes from './util/includesArray';
 
 /**
  * List of country codes with
@@ -131,7 +132,7 @@ function hasValidIbanFormat(str, options) {
       return false;
     }
 
-    const isoCountryCodeInWhiteList = options.whitelist.includes(isoCountryCode);
+    const isoCountryCodeInWhiteList = includes(options.whitelist, isoCountryCode);
 
     if (!isoCountryCodeInWhiteList) {
       return false;
@@ -139,7 +140,7 @@ function hasValidIbanFormat(str, options) {
   }
 
   if (options.blacklist) {
-    const isoCountryCodeInBlackList = options.blacklist.includes(isoCountryCode);
+    const isoCountryCodeInBlackList = includes(options.blacklist, isoCountryCode);
 
     if (isoCountryCodeInBlackList) {
       return false;
diff --git a/src/lib/isIdentityCard.js b/src/lib/isIdentityCard.js
index 060618f..fc37d4a 100644
--- a/src/lib/isIdentityCard.js
+++ b/src/lib/isIdentityCard.js
@@ -1,4 +1,5 @@
 import assertString from './util/assertString';
+import includes from './util/includesArray';
 import isInt from './isInt';
 
 const validators = {
@@ -277,7 +278,7 @@ const validators = {
 
     const parityBit = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
 
-    const checkAddressCode = addressCode => provincesAndCities.includes(addressCode);
+    const checkAddressCode = addressCode => includes(provincesAndCities, addressCode);
 
     const checkBirthDayCode = (birDayCode) => {
       const yyyy = parseInt(birDayCode.substring(0, 4), 10);
diff --git a/src/lib/isJSON.js b/src/lib/isJSON.js
index d3731e3..5c51dd3 100644
--- a/src/lib/isJSON.js
+++ b/src/lib/isJSON.js
@@ -1,4 +1,5 @@
 import assertString from './util/assertString';
+import includes from './util/includesArray';
 import merge from './util/merge';
 
 const default_json_options = {
@@ -15,7 +16,7 @@ export default function isJSON(str, options) {
     }
 
     const obj = JSON.parse(str);
-    return primitives.includes(obj) || (!!obj && typeof obj === 'object');
+    return includes(primitives, obj) || (!!obj && typeof obj === 'object');
   } catch (e) { /* ignore */ }
   return false;
 }
diff --git a/src/lib/isLatLong.js b/src/lib/isLatLong.js
index 5c53c23..c4e6227 100644
--- a/src/lib/isLatLong.js
+++ b/src/lib/isLatLong.js
@@ -1,5 +1,6 @@
 import assertString from './util/assertString';
 import merge from './util/merge';
+import includes from './util/includesString';
 
 const lat = /^\(?[+-]?(90(\.0+)?|[1-8]?\d(\.\d+)?)$/;
 const long = /^\s?[+-]?(180(\.0+)?|1[0-7]\d(\.\d+)?|\d{1,2}(\.\d+)?)\)?$/;
@@ -15,7 +16,7 @@ export default function isLatLong(str, options) {
   assertString(str);
   options = merge(options, defaultLatLongOptions);
 
-  if (!str.includes(',')) return false;
+  if (!includes(str, ',')) return false;
   const pair = str.split(',');
   if ((pair[0].startsWith('(') && !pair[1].endsWith(')'))
     || (pair[1].endsWith(')') && !pair[0].startsWith('('))) return false;
diff --git a/src/lib/isPostalCode.js b/src/lib/isPostalCode.js
index 01ae3fc..5472346 100644
--- a/src/lib/isPostalCode.js
+++ b/src/lib/isPostalCode.js
@@ -57,6 +57,8 @@ const patterns = {
   NO: fourDigit,
   NP: /^(10|21|22|32|33|34|44|45|56|57)\d{3}$|^(977)$/i,
   NZ: fourDigit,
+  // https://www.pakpost.gov.pk/postcodes.php
+  PK: fiveDigit,
   PL: /^\d{2}\-\d{3}$/,
   PR: /^00[679]\d{2}([ -]\d{4})?$/,
   PT: /^\d{4}\-\d{3}?$/,
@@ -69,7 +71,7 @@ const patterns = {
   SK: /^\d{3}\s?\d{2}$/,
   TH: fiveDigit,
   TN: fourDigit,
-  TW: /^\d{3}(\d{2})?$/,
+  TW: /^\d{3}(\d{2,3})?$/,
   UA: fiveDigit,
   US: /^\d{5}(-\d{4})?$/,
   ZA: fourDigit,
diff --git a/src/lib/isURL.js b/src/lib/isURL.js
index 9bfafbf..24ac452 100644
--- a/src/lib/isURL.js
+++ b/src/lib/isURL.js
@@ -1,5 +1,6 @@
 import assertString from './util/assertString';
 import checkHost from './util/checkHost';
+import includes from './util/includesString';
 
 import isFQDN from './isFQDN';
 import isIP from './isIP';
@@ -53,11 +54,11 @@ export default function isURL(url, options) {
     return false;
   }
 
-  if (!options.allow_fragments && url.includes('#')) {
+  if (!options.allow_fragments && includes(url, '#')) {
     return false;
   }
 
-  if (!options.allow_query_components && (url.includes('?') || url.includes('&'))) {
+  if (!options.allow_query_components && (includes(url, '?') || includes(url, '&'))) {
     return false;
   }
 
diff --git a/src/lib/util/includes.js b/src/lib/util/includesArray.js
similarity index 100%
rename from src/lib/util/includes.js
rename to src/lib/util/includesArray.js
diff --git a/src/lib/util/includesString.js b/src/lib/util/includesString.js
new file mode 100644
index 0000000..41a50bb
--- /dev/null
+++ b/src/lib/util/includesString.js
@@ -0,0 +1,3 @@
+const includes = (str, val) => str.indexOf(val) !== -1;
+
+export default includes;



Test patch:
diff --git a/test/validators.test.js b/test/validators.test.js
index 1559eaa..72b5591 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -12576,6 +12576,7 @@ describe('Validators', () => {
           '399',
           '935',
           '38842',
+          '546023',
         ],
       },
       {
@@ -12658,6 +12659,22 @@ describe('Validators', () => {
           '4144',
         ],
       },
+      {
+        locale: 'PK',
+        valid: [
+          '25000',
+          '44000',
+          '54810',
+          '74200',
+        ],
+        invalid: [
+          '5400',
+          '540000',
+          'NY540',
+          '540CA',
+          '540-0',
+        ],
+      },
       {
         locale: 'MG',
         valid: [