diff --git a/README.md b/README.md
index 8ef42fd79..2d99bd4bb 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 an [ISMN][ISMN]. Supports the modern 13-digit format beginning with `979-0` and the legacy 10-character format beginning with `M`.
**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.
@@ -251,6 +252,7 @@ This project is licensed under the [MIT](LICENSE). See the [LICENSE](LICENSE) fi
[IMEI]: https://en.wikipedia.org/wiki/International_Mobile_Equipment_Identity
[ISBN]: https://en.wikipedia.org/wiki/ISBN
[ISIN]: https://en.wikipedia.org/wiki/International_Securities_Identification_Number
+[ISMN]: https://ismn-international.org/ismn/the-ismn/
[ISO 639-1]: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
[ISO 8601]: https://en.wikipedia.org/wiki/ISO_8601
[ISO 15924]: https://en.wikipedia.org/wiki/ISO_15924
diff --git a/src/index.js b/src/index.js
index 3700cbd6c..92272bca8 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';
@@ -200,6 +201,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..9b0b9a0c1
--- /dev/null
+++ b/src/lib/isISMN.js
@@ -0,0 +1,27 @@
+import assertString from './util/assertString';
+
+const possibleIsmn10 = /^M[0-9]{9}$/;
+const possibleIsmn13 = /^9790[0-9]{9}$/;
+const factor = [1, 3];
+
+function hasValidCheckDigit(ismn) {
+ let checksum = 0;
+
+ for (let i = 0; i < 12; i++) {
+ checksum += factor[i % 2] * Number(ismn.charAt(i));
+ }
+
+ return Number(ismn.charAt(12)) === ((10 - (checksum % 10)) % 10);
+}
+
+export default function isISMN(ismn) {
+ assertString(ismn);
+
+ const sanitizedIsmn = ismn.replace(/[\s-]+/g, '').toUpperCase();
+
+ if (possibleIsmn10.test(sanitizedIsmn)) {
+ return hasValidCheckDigit(`9790${sanitizedIsmn.slice(1)}`);
+ }
+
+ return possibleIsmn13.test(sanitizedIsmn) && hasValidCheckDigit(sanitizedIsmn);
+}
diff --git a/test/validators/isISMN.test.js b/test/validators/isISMN.test.js
new file mode 100644
index 000000000..f17099ee8
--- /dev/null
+++ b/test/validators/isISMN.test.js
@@ -0,0 +1,37 @@
+import test from '../testFunctions';
+
+describe('isISMN', () => {
+ it('should validate ISMNs', () => {
+ test({
+ validator: 'isISMN',
+ valid: [
+ '9790345246805',
+ '979-0-345-24680-5',
+ '979 0 345 24680 5',
+ '9790215901001',
+ '979-0-2159-0100-1',
+ '979 0 2159 0100 1',
+ 'M345246805',
+ 'M-345-24680-5',
+ 'M 345 24680 5',
+ 'm-345-24680-5',
+ 'M230671187',
+ 'M-2306-7118-7',
+ ],
+ invalid: [
+ '9790345246800',
+ '979-0-345-24680-0',
+ '979-1-345-24680-5',
+ '9780345246805',
+ 'M345246800',
+ 'M-345-24680-0',
+ 'M-345-24680-X',
+ 'M-345-2468A-5',
+ '979034524680',
+ '97903452468055',
+ 'foo',
+ '',
+ ],
+ });
+ });
+});