diff --git a/README.md b/README.md
index e8b7aedd9..ef37f9707 100644
--- a/README.md
+++ b/README.md
@@ -127,6 +127,7 @@ Validator | Description
**isIPRange(str [, version])** | check if the string is an IP Range (version 4 or 6).
**isISBN(str [, options])** | check if the string is an [ISBN][ISBN].
`options` is an object that has no default.
**Options:**
`version`: ISBN version to compare to. Accepted values are '10' and '13'. If none provided, both will be tested.
**isISIN(str)** | check if the string is an [ISIN][ISIN] (stock/security identifier).
+**isISMN(str)** | check if the string is a valid International Standard Music Number (ISMN). Supports both 13-digit and older 10-character ISMNs, allows spaces or hyphens, and checks the check digit.
**isISO6346(str)** | check if the string is a valid [ISO 6346](https://en.wikipedia.org/wiki/ISO_6346) shipping container identification.
**isISO6391(str)** | check if the string is a valid [ISO 639-1][ISO 639-1] language code.
**isISO8601(str [, options])** | check if the string is a valid [ISO 8601][ISO 8601] date.
`options` is an object which defaults to `{ strict: false, strictSeparator: false }`. If `strict` is true, date strings with invalid dates like `2009-02-29` will be invalid. If `strictSeparator` is true, date strings with date and time separated by anything other than a T will be invalid.
diff --git a/src/index.js b/src/index.js
index 5e2cd78a7..0dddff74b 100644
--- a/src/index.js
+++ b/src/index.js
@@ -79,6 +79,7 @@ import isIdentityCard from './lib/isIdentityCard';
import isEAN from './lib/isEAN';
import isISIN from './lib/isISIN';
import isISBN from './lib/isISBN';
+import isISMN from './lib/isISMN';
import isISSN from './lib/isISSN';
import isTaxID from './lib/isTaxID';
@@ -201,6 +202,7 @@ const validator = {
isEAN,
isISIN,
isISBN,
+ isISMN,
isISSN,
isMobilePhone,
isMobilePhoneLocales,
diff --git a/src/lib/isISMN.js b/src/lib/isISMN.js
new file mode 100644
index 000000000..5282bb7eb
--- /dev/null
+++ b/src/lib/isISMN.js
@@ -0,0 +1,38 @@
+import assertString from './util/assertString';
+
+export default function isISMN(str) {
+ assertString(str);
+
+ // Check for spaces or hyphens at the beginning, end, or repeated together.
+ if (/^[ -]|[ -]$|[ -]{2}/.test(str)) {
+ return false;
+ }
+
+ // Remove spaces and hyphens so we can check the number.
+ let ismn = str.replace(/[ -]/g, '');
+
+ // Change the older ISMN format to the 13-digit version.
+ if (/^M[0-9]{9}$/.test(ismn)) {
+ ismn = `9790${ismn.slice(1)}`;
+ }
+
+ // Make sure it has 13 digits and starts with 9790.
+ if (!/^9790[0-9]{9}$/.test(ismn)) {
+ return false;
+ }
+
+ // Go through the first 12 digits to calculate the check digit.
+ let sum = 0;
+
+ for (let i = 0; i < 12; i++) {
+ const digit = Number(ismn[i]);
+ const weight = i % 2 === 0 ? 1 : 3;
+ sum += digit * weight;
+ }
+
+ // Find what the last digit should be.
+ const checkDigit = (10 - (sum % 10)) % 10;
+
+ // Check if the last digit matches what we calculated.
+ return checkDigit === Number(ismn[12]);
+}
diff --git a/test/validators.test.js b/test/validators.test.js
index 98d2a12ff..1fb4e047a 100644
--- a/test/validators.test.js
+++ b/test/validators.test.js
@@ -7167,6 +7167,64 @@ describe('Validators', () => {
});
});
+ it('should validate ISMN numbers', () => {
+ test({
+ validator: 'isISMN',
+ valid: [
+ // Modern 13-digit ISMNs
+ '9790260000438',
+ '9790000000001',
+ '9790692006282',
+ '9790345246805',
+
+ // ISMNs with spaces or hyphens
+ '979-0-2600-0043-8',
+ '979 0 2600 0043 8',
+ '979-0-69200-628-2',
+
+ // Older ISMNs starting with M
+ 'M571100513',
+ 'M692006282',
+ 'M345246805',
+ 'M-345-24680-5',
+ 'M 345 24680 5',
+ ],
+ invalid: [
+ // Numbers with the wrong check digit
+ '9790260000439',
+ '9790692006283',
+ 'M692006283',
+
+ // Numbers that start incorrectly
+ '9780260000438',
+ '9791260000438',
+ 'A692006282',
+
+ // Numbers that are too short or too long
+ '979026000043',
+ '97902600004388',
+ 'M69200628',
+ 'M6920062822',
+
+ // Numbers with invalid characters
+ '9790A60000438',
+ 'M69200A282',
+ '9790.2600.0043.8',
+ '',
+
+ // Incorrect spaces or hyphens
+ '-9790260000438',
+ '9790260000438-',
+ '979--0-2600-0043-8',
+ 'M--345-24680-5',
+ ],
+ error: [
+ null,
+ 9790260000438,
+ ],
+ });
+ });
+
it('should validate ISSNs', () => {
test({
validator: 'isISSN',