UNPKG

libphonenumber-metadata-generator

Version:

Metadata generator for `libphonenumber-js`

821 lines (702 loc) 35.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports["default"] = _default; var _xml2js = require("xml2js"); function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); } function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _iterableToArrayLimit(arr, i) { if (typeof Symbol === "undefined" || !(Symbol.iterator in Object(arr))) return; var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; } function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e2) { throw _e2; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e3) { didErr = true; err = _e3; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; } // Should be the same as `DIGIT_PLACEHOLDER` in `libphonenumber-js`. var DIGIT_PLACEHOLDER = 'x'; var PHONE_NUMBER_TYPES = ['premium_rate', 'toll_free', 'shared_cost', 'voip', 'personal_number', 'pager', 'uan', 'voicemail', 'fixed_line', 'mobile']; // Excessive fields from "PhoneNumberMetadata.xml" // aren't included to reduce code complexity and size: // // * `<references>` — a link to ITU (International Telecommunication Union) // document describing phone numbering plan for a country // // * `<noInternationalDialling>` — who needs to input non-internationally-dialable phones // // * `<areaCodeOptional>` — we aren't in the XXth century, // it's a globalized world, so write your // phone numbers with area codes. // // * `<fixedLine>`, `<mobile>`, `<pager>`, // `<tollFree>`, `<premiumRate>`, // `<sharedCost>`, `<personalNumber>`, // `<voip>`, `<uan>`, `<voicemail>` — who needs that in the XXIst century. // just go mobile and stop talking nonsense. // // * `internationalPrefix`, // `preferredInternationalPrefix` — who needs to parse (or format) those weird // "internationally dialed" phone numbers // like "011 ..." in the USA. // this isn't XXth century, just use mobile phones. // // * `preferredExtnPrefix` — Localized " ext. ". E.g. ", доб. " instead of " ext. " for Russia. // // * `leadingZeroPossible` — (aka "italian leading zero") // who needs to parse a phone number into an integer. // just keep it as a string. // // * `carrierCodeFormattingRule` — only used in Brazil and Colombia // when dialing from within those countries // from mobile phones to fixed line phone numbers. // i guess brazilians and colombians // already know when to add those carrier codes // by themselves (and when not to add them) // // * `mobileNumberPortableRegion` — is only used to disable phone number type detection // // * `<possibleLengths>` — is a redundant field to speed up testing of // whether a phone number format can be used to format // a particular national (significant) phone number. // // `libphonenumber/BuildMetadataFromXml.java` was used as a reference. // https://github.com/googlei18n/libphonenumber/blob/master/tools/java/common/src/com/google/i18n/phonenumbers/BuildMetadataFromXml.java // // There are three Xml metadata files in Google's `libphonenumber`: // // * PhoneNumberMetadata.xml — core data, used both for parse/format and "as you type" // // * PhoneNumberAlternateFormats.xml — alternative phone number formats. // is presumably used for parsing phone numbers // written in "alternative" formats. // is not used by "as you type" // presumably because of formats ambiguity // when combined with the core data. // this metadata is not used in this library // as there's no clear description on what to do with it // and how it works in the original `libphonenumber` code. // // * ShortNumberMetadata.xml — emergency numbers, etc. not used in this library. // // @returns // // { // country_calling_codes: // { // '7': ['RU', 'KZ', ...], // ... // }, // countries: // { // RU: // { // phone_code: "7", // national_number_pattern: "[347-9]\\d{9}", // national_prefix: "8", // national_prefix_formatting_rule: "8 ($1)", // national_prefix_is_optional_when_formatting: true, // types: // { // fixed_line: "(?:3(?:0[12]|4[1-35-79]|5[1-3]|65|8[1-58]|9[0145])|4(?:01|1[1356]|2[13467]|7[1-5]|8[1-7]|9[1-689])|8(?:1[1-8]|2[01]|3[13-6]|4[0-8]|5[15]|6[1-35-79]|7[1-37-9]))\\d{7}", // mobile: "9\\d{9}", // ... // }, // examples: // { // fixed_line: '4955553535', // mobile: '9991234567', // ... // }, // formats: // [{ // pattern: "([3489]\\d{2})(\\d{3})(\\d{2})(\\d{2})", // leading_digits_patterns: ["[3489]"], // format: "$1 $2-$3-$4" // }, // ...] // }, // ... // } // } // // `country_calling_codes` map is kinda redundant. // Not sure why did I choose to place country phone codes // into a separate structure inside metadata instead of generating it in runtime. // One extra feature it gives though is it tells what's the // "default" country for a given country phone code. // E.g. for country phone code `1` the "default" country is "US" // and therefore "US" is the first country code in the // `country_calling_codes["1"]` list. // The "default" country is the one other countries // with the same country phone code inherit phone number formatting rules from. // For example, "CA" (Canada) inhertis phone number formatting rules from "US". // // `country_calling_codes` data takes about 3 KiloBytes // so it could kinda make sense to drop it from the metadata file // replacing it with a "default" country flag (something like `1` for "yes"). // In that scenario `country_calling_codes` would be generated on startup. // It would have to also provide an exported `getCountryPhoneCodes()` function // which would take `metadata` and return `country_calling_codes` map // because some people use that `country_calling_codes` map in their projects. // // On the other hand, having `country_calling_codes` // prepopulated yields more elegance to the exports // because if `country_calling_codes` wasn't part of metadata // it would have to be computed somewhere in global scope // therefore the modules wouldn't be strictly "pure" // so maybe `country_calling_codes` stays as part of metadata. // function _default(xmlString, version, includedCountries, extended, includedPhoneNumberTypes) { // Validate `includedPhoneNumberTypes` if (includedPhoneNumberTypes) { var _iterator = _createForOfIteratorHelper(includedPhoneNumberTypes), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { var _type = _step.value; if (PHONE_NUMBER_TYPES.indexOf(_type) < 0) { return Promise.reject("Unknown phone number type: ".concat(_type)); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } // Parse the XML metadata. // See: https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/METADATA.md return parseXmlString(xmlString).then(function (xml) { // https://github.com/googlei18n/libphonenumber/blob/master/resources/PhoneNumberMetadata.xml // https://github.com/googlei18n/libphonenumber/blob/master/resources/phonemetadata.proto // https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js // https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/asyoutypeformatter.js var country_calling_code_to_countries = {}; var countries = {}; var nonGeographic = {}; var _iterator2 = _createForOfIteratorHelper(xml.phoneNumberMetadata.territories[0].territory), _step2; try { var _loop = function _loop() { var territory = _step2.value; // A two-letter country code var country_code = territory.$.id; // Skip this country if it has not been explicitly included if (includedCountries && !includedCountries.has(country_code)) { return "continue"; } if (territory.$.nationalPrefixOptionalWhenFormatting) { throw new Error('`nationalPrefixOptionalWhenFormatting` encountered on a country level: uncomment the relevant code part below.'); } // Country metadata var country = { // Phone code related fields: // Phone code for phone numbers in this country. // // E.g. `1` for both USA and Canada. // phone_code: territory.$.countryCode, // International Direct Dialing prefix. idd_prefix: territory.$.internationalPrefix, default_idd_prefix: territory.$.preferredInternationalPrefix, // Localized " ext. " prefix. ext: territory.$.preferredExtnPrefix, // In case of several countries // having the same country phone code, // these leading digits are the means // of classifying an international phone number // whether it belongs to a certain country. // // E.g. for Antigua and Barbuda // country phone code is `1` (same as USA) // and leading digits are `268`. // leading_digits: territory.$.leadingDigits, // The regular expression of all possible // national (significant) numbers for this country. national_number_pattern: territory.generalDesc[0].nationalNumberPattern[0].replace(/\s/g, ''), // National prefix related fields: // aka "trunk code". // This is the prefix prepended to a // national (significant) phone number // when dialed from within the country. // E.g. `0` for UK. national_prefix: territory.$.nationalPrefix, // In some (many) countries the national prefix // is not just a constant digit (like `0` in UK) // but can be different depending on the phone number // (and can be also absent for some phone numbers). // // So `national_prefix_for_parsing` is used when parsing // a national-prefixed (local) phone number // into a national significant phone number // extracting that possible national prefix out of it. // national_prefix_for_parsing: territory.$.nationalPrefixForParsing ? territory.$.nationalPrefixForParsing.replace(/\s/g, '') : undefined, // If `national_prefix_for_parsing` regular expression // contains "captured groups", then `national_prefix_transform_rule` // defines how the national-prefixed (local) phone number is // parsed into a national significant phone number. // // Pseudocode: // // national_prefix_pattern = regular_expression('^(?:' + national_prefix_for_parsing + ')') // national_significant_number = all_digits.replace(national_prefix_pattern, national_prefix_transform_rule) // // E.g. if a country's national numbers are 6-digit // and national prefix is always `0`, // then `national_prefix_for_parsing` could be `0(\d{6})` // and the corresponding `national_prefix_transform_rule` would be `$1` // (which is the default behaviour). // // Currently this feature is only used in // Argentina, Brazil, Mexico and San Marino // due to their messy telephone numbering plans. // // For example, mobile numbers in Argentina are written in two completely // different ways when dialed in-country and out-of-country // (e.g. 0343 15-555-1212 is exactly the same number as +54 9 3435 55 1212). // Therefore for Argentina `national_prefix_transform_rule` is `9$1`. // national_prefix_transform_rule: territory.$.nationalPrefixTransformRule, // Controls how national prefix is written // in a formatted local phone number. // // E.g. in Armenia national prefix is `0` // and `national_prefix_formatting_rule` is `($NP$FG)` // which means that a national significant phone number `xxxxxxxx` // matching phone number pattern `(\d{2})(\d{6})` with format `$1 $2` // is written as a local phone number `(0xx) xxxxxx`. // // Can be `undefined`. // // Maybe rename this property to `national_prefix_transform_rule`. // national_prefix_formatting_rule: getNationalPrefixFormattingRule(territory.$.nationalPrefixFormattingRule, territory.$.nationalPrefix), // Is it possible that a national (significant) // phone number has leading zeroes? // // E.g. in Gabon some numbers start with a `0` // while the national prefix is also `0` // which is optional for mobile numbers. // // This seems to only be used for validating // possible formats in AsYouType formatter. // // national_prefix_is_optional_when_formatting: territory.$.nationalPrefixOptionalWhenFormatting ? Boolean(territory.$.nationalPrefixOptionalWhenFormatting) : undefined, // In some countries carrier code is required // to dial certain phone numbers. // // E.g. in Colombia calling to fixed line numbers // from mobile phones requires a carrier code when called within Colombia. // Or, for example, Brazilian fixed line and mobile numbers // need to be dialed with a carrier code when called within Brazil. // Without that, most of the carriers won't connect the call. // domestic_carrier_code_formatting_rule: territory.$.carrierCodeFormattingRule, // These `types` will be purged later, // if they're not needed (which is most likely). // See `country_calling_code_to_countries` ambiguity for more info. // types: get_PHONE_NUMBER_TYPES(territory), // Will be filtered out during compression phase examples: get_phone_number_examples(territory) }; // Check that national (significant) phone number pattern // is set for this country (no "default" value here) if (!country.national_number_pattern) { throw new Error("\"generalDesc.nationalNumberPattern\" is missing for country ".concat(country_code, " metadata")); } // Check that an IDD prefix is always defined. if (country_code !== '001' && !country.idd_prefix) { throw new Error("\"generalDesc.internationalPrefix\" is missing for country ".concat(country_code, " metadata")); } // // Check that a preferred IDD prefix is always defined if IDD prefix is a pattern. // if (country_code !== '001' && !isSingleIDDPrefix(country.idd_prefix) && !country.default_idd_prefix) { // throw new Error(`"generalDesc.preferredInternationalPrefix" is missing for country ${country_code} metadata`) // } // Some countries don't have `availableFormats` specified, // because those formats are inherited from the "main country for region": // all non-"main" countries inherit their formats from the "main" country for that region. if (territory.availableFormats) { country.formats = territory.availableFormats[0].numberFormat.map(function (number_format) { return { pattern: number_format.$.pattern, leading_digits_patterns: number_format.leadingDigits ? number_format.leadingDigits.map(function (leading_digits) { return leading_digits.replace(/\s/g, ''); }) : undefined, national_prefix_formatting_rule: getNationalPrefixFormattingRule(number_format.$.nationalPrefixFormattingRule, territory.$.nationalPrefix), national_prefix_is_optional_when_formatting: number_format.$.nationalPrefixOptionalWhenFormatting ? Boolean(number_format.$.nationalPrefixOptionalWhenFormatting) : undefined, format: number_format.format[0], international_format: number_format.intlFormat ? number_format.intlFormat[0] : undefined, domestic_carrier_code_formatting_rule: number_format.$.carrierCodeFormattingRule }; }) // Screw local-only formats .filter(function (format) { return format.international_format !== 'NA'; }); // Sanity check (using no "default" for this field) var _iterator6 = _createForOfIteratorHelper(country.formats), _step6; try { for (_iterator6.s(); !(_step6 = _iterator6.n()).done;) { var format = _step6.value; // Never happens if (!format.format) { throw new Error("No phone number format \"format\" supplied for pattern ".concat(format.pattern, " for ").concat(country_code)); } // Never happens if (format.format.indexOf(DIGIT_PLACEHOLDER) >= 0) { throw new Error("Phone number format \"".concat(format.format, "\" contains a reserved \"").concat(DIGIT_PLACEHOLDER, "\" symbol for pattern ").concat(format.pattern, " for ").concat(country_code)); } } } catch (err) { _iterator6.e(err); } finally { _iterator6.f(); } } if (country_code === '001') { nonGeographic[country.phone_code] = country; // Populate numbering plan possible lengths. populateNumberingPlanPossibleLengths(country); } else { // Add this country's metadata // to the metadata map. countries[country_code] = country; // Register this country's "country phone code" if (!country_calling_code_to_countries[country.phone_code]) { country_calling_code_to_countries[country.phone_code] = []; } // In case of several countries // having the same country phone code. // // E.g. for USA and Canada, USA is the // "main country for phone code 1". // // (maybe this field is not used at all // in which case this field is to be removed) // if (territory.$.mainCountryForCode === "true") { country_calling_code_to_countries[country.phone_code].unshift(country_code); } else { country_calling_code_to_countries[country.phone_code].push(country_code); } } }; for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) { var _ret = _loop(); if (_ret === "continue") continue; } // Some countries don't have `availableFormats` specified, // because those formats are meant to be copied from the "main country for region": // all non-"main" countries inherit their formats from the "main" country for that region. // If that's the case then `nationalPrefixFormattingRule` and // `nationalPrefixOptionalWhenFormatting` are also copied from the "main" region. // `nationalPrefix` itself though seems to be always present // even if it's the same for the "main" region. // Examples: "RU" and "KZ", "US" and "CA". } catch (err) { _iterator2.e(err); } finally { _iterator2.f(); } for (var _i = 0, _Object$keys = Object.keys(countries); _i < _Object$keys.length; _i++) { var country_code = _Object$keys[_i]; var country = countries[country_code]; var main_country_for_region_code = country_calling_code_to_countries[country.phone_code][0]; if (main_country_for_region_code === country_code) { // Some countries like Saint Helena and Falkland Islands // ('AC', 'FK', 'KI', 'NU', 'SH', 'TA', ...) // don't have any phone number formats defined // and phone numbers are not formatted in those countries. if (!country.formats) { country.formats = []; } } else { if (country.formats !== undefined) { throw new Error("Country \"".concat(country_code, "\" is supposed to inherit formats from \"").concat(main_country_for_region_code, "\" but has its own formats defined.")); } if (country.national_prefix_formatting_rule !== undefined) { throw new Error("Country \"".concat(country_code, "\" is supposed to inherit \"national_prefix_formatting_rule\" from \"").concat(main_country_for_region_code, "\" but has its own \"national_prefix_formatting_rule\" defined.")); } if (country.national_prefix_is_optional_when_formatting !== undefined) { throw new Error("Country \"".concat(country_code, "\" is supposed to inherit \"national_prefix_is_optional_when_formatting\" from \"").concat(main_country_for_region_code, "\" but has its own \"national_prefix_is_optional_when_formatting\" defined.")); } } } // Turns out that `<generalDesc><nationalNumberPattern/></generalDesc>` // is not preemptive at all: it's too unspecific for the cases // when several countries correspond to the same country phone code // (e.g. NANPA: US, Canada, etc — all correspond to the same `1` country phone code). // For these cases all those bulky `<fixedLine/>`, `<mobile/>`, etc // patterns are required. Therefore retain them for these rare cases. // // This increases metadata size by 5 KiloBytes. // var visited_countries = {}; for (var _i2 = 0, _Object$keys2 = Object.keys(country_calling_code_to_countries); _i2 < _Object$keys2.length; _i2++) { var country_calling_code = _Object$keys2[_i2]; var country_codes = country_calling_code_to_countries[country_calling_code]; var _iterator3 = _createForOfIteratorHelper(country_codes), _step3; try { for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) { var _country_code = _step3.value; if (visited_countries[_country_code]) { continue; } visited_countries[_country_code] = true; // Populate numbering plan possible lengths. populateNumberingPlanPossibleLengths(countries[_country_code]); if (countries[_country_code].possible_lengths.length === 0) { throw new Error("No \"possibleLengths\" set for country \"".concat(_country_code, "\". \"react-phone-number-input\" relies on \"possibleLengths\" being always present.")); } } // Purge `types` regular expressions (they are huge) // when they're not needed for resolving country phone code // to country phone number matching. // E.g. when there's a one-to-one correspondence // between a country phone code and a country code } catch (err) { _iterator3.e(err); } finally { _iterator3.f(); } var all_types_required = country_codes.length > 1; if (!extended && !includedPhoneNumberTypes && !all_types_required) { delete countries[country_codes[0]].types; continue; } var _iterator4 = _createForOfIteratorHelper(country_codes), _step4; try { for (_iterator4.s(); !(_step4 = _iterator4.n()).done;) { var _country_code2 = _step4.value; // Leading digits for a country are sufficient // to resolve country phone code ambiguity. // So retaining all phone number type regular expressions // is not required in this case. if (!extended && !includedPhoneNumberTypes) { if (countries[_country_code2].leading_digits) { delete countries[_country_code2].types; continue; } } var types = countries[_country_code2].types; // Find duplicate regular expressions for types // and just discard such duplicate types // to reduce metadata size (by 5 KiloBytes). // Or retain regular expressions just for the // specified phone number types (if configured). var _iterator5 = _createForOfIteratorHelper(PHONE_NUMBER_TYPES), _step5; try { for (_iterator5.s(); !(_step5 = _iterator5.n()).done;) { var _type2 = _step5.value; if (!types[_type2]) { continue; } // Retain regular expressions just for the // specified phone number types (if configured). if (includedPhoneNumberTypes) { if (!includedPhoneNumberTypes.has(_type2)) { delete types[_type2]; } } // Remove redundant types // (other types having the same regular expressions as this one) else { // Sometimes fixed line pattern is the same as for mobile. if (types.fixed_line && types.mobile && types.fixed_line.pattern === types.mobile.pattern) { types.mobile.pattern = ''; } } } } catch (err) { _iterator5.e(err); } finally { _iterator5.f(); } } } catch (err) { _iterator4.e(err); } finally { _iterator4.f(); } } return { version: version, countries: countries, country_calling_codes: country_calling_code_to_countries, nonGeographic: nonGeographic }; }); } // Replaces $NP with national prefix and $FG with the first group ($1) function getNationalPrefixFormattingRule(rule, national_prefix) { if (!rule) { return; } // Replace $NP with national prefix and $FG with the first group ($1) return rule.replace('$NP', national_prefix).replace('$FG', '$1'); } // Extracts various phone number type patterns from country XML metadata function get_PHONE_NUMBER_TYPES(territory) { return PHONE_NUMBER_TYPES.reduce(function (output, type) { var camel_cased_type = underscoreToCamelCase(type); var pattern = territory[camel_cased_type] && territory[camel_cased_type][0].nationalNumberPattern[0].replace(/\s/g, ''); var possible_lengths = territory[camel_cased_type] && territory[camel_cased_type][0].possibleLengths[0].$.national; var possible_lengths_local = territory[camel_cased_type] && territory[camel_cased_type][0].possibleLengths[0].$.localOnly; if (pattern) { output[type] = { pattern: pattern, possible_lengths: possible_lengths // possible_lengths_local }; } return output; }, {}); } // Extracts various phone number type examples from country XML metadata function get_phone_number_examples(territory) { return PHONE_NUMBER_TYPES.reduce(function (output, type) { var camel_cased_type = underscoreToCamelCase(type); var example = territory[camel_cased_type] && territory[camel_cased_type][0].exampleNumber[0]; if (example) { output[type] = example; } return output; }, {}); } function underscoreToCamelCase(string) { return string.replace(/(\_\w)/g, function (match) { return match[1].toUpperCase(); }); } /** * Parses a possible length string into a set of the integers that are covered. * * @param {string} possible_length_string - A string specifying the possible lengths of phone numbers. Follows * this syntax: ranges or elements are separated by commas, and ranges are specified in * [min-max] notation, inclusive. For example, [3-5],7,9,[11-14] should be parsed to * 3,4,5,7,9,11,12,13,14. * @return {Set} */ function parse_possible_lengths(possible_length_string) { if (possible_length_string.length === 0) { throw new TypeError('Empty possibleLength string found.'); } var lengths = new Set(); var _iterator7 = _createForOfIteratorHelper(possible_length_string.split(',')), _step7; try { for (_iterator7.s(); !(_step7 = _iterator7.n()).done;) { var length = _step7.value; if (length.length == 0) { throw new TypeError("Leading, trailing or adjacent commas in possible length string ".concat(length, ", these should only separate numbers or ranges.")); } if (length[0] === '[') { if (length[length.length - 1] !== ']') { throw new TypeError("Missing end of range character in possible length string ".concat(length, ".")); } // Strip the leading and trailing [], and split on the -. var min_max = length.slice(1, length.length - 1).split('-').map(function (_) { return parseInt(_); }); if (min_max.length !== 2) { throw new TypeError("Ranges must have exactly one - character: missing for ".concat(length, ".")); } var _min_max = _slicedToArray(min_max, 2), min = _min_max[0], max = _min_max[1]; // We don't even accept [6-7] since we prefer the shorter 6,7 variant; // for a range to be in use the hyphen needs to replace at least one digit. if (max - min < 2) { throw new TypeError("The first number in a range should be two or more digits lower than the second. Culprit possibleLength string: ".concat(length)); } for (var i = min; i <= max; i++) { if (lengths.has(i)) { throw new TypeError("Duplicate length element found (".concat(i, ") in possibleLength string ").concat(length, ".")); } lengths.add(i); } } else { var _i3 = parseInt(length); if (lengths.has(_i3)) { throw new TypeError("Duplicate length element found (".concat(_i3, ") in possibleLength string ").concat(length, ".")); } lengths.add(_i3); } } } catch (err) { _iterator7.e(err); } finally { _iterator7.f(); } return lengths; } var areEqualArrays = function areEqualArrays(a1, a2) { return a1.length === a2.length && a1.every(function (_, i) { return _ === a2[i]; }); }; /** * Sets `metadata.possible_lengths` to a combination of `possible_length`s * of all types present in the numbering plan metadata. * @param {object} metadata */ function populateNumberingPlanPossibleLengths(metadata) { var types = metadata.types; var possible_lengths = new Set(); var possible_lengths_local = new Set(); for (var _i4 = 0, _Object$keys3 = Object.keys(types); _i4 < _Object$keys3.length; _i4++) { var _type = _Object$keys3[_i4]; var type_possible_lengths = parse_possible_lengths(types[_type].possible_lengths); var _iterator8 = _createForOfIteratorHelper(type_possible_lengths), _step8; try { for (_iterator8.s(); !(_step8 = _iterator8.n()).done;) { var _i5 = _step8.value; possible_lengths.add(_i5); } } catch (err) { _iterator8.e(err); } finally { _iterator8.f(); } types[_type].possible_lengths = Array.from(type_possible_lengths); if (types[_type].possible_lengths_local) { var type_possible_lengths_local = parse_possible_lengths(types[_type].possible_lengths_local); var _iterator9 = _createForOfIteratorHelper(type_possible_lengths_local), _step9; try { for (_iterator9.s(); !(_step9 = _iterator9.n()).done;) { var i = _step9.value; possible_lengths_local.add(i); } } catch (err) { _iterator9.e(err); } finally { _iterator9.f(); } types[_type].possible_lengths_local = Array.from(type_possible_lengths_local); } } var _iterator10 = _createForOfIteratorHelper(possible_lengths_local), _step10; try { for (_iterator10.s(); !(_step10 = _iterator10.n()).done;) { var _i7 = _step10.value; if (possible_lengths.has(_i7)) { possible_lengths_local["delete"](_i7); } } } catch (err) { _iterator10.e(err); } finally { _iterator10.f(); } metadata.possible_lengths = Array.from(possible_lengths); metadata.possible_lengths.sort(function (a, b) { return a - b; }); if (possible_lengths_local.size > 0) { metadata.possible_lengths_local = Array.from(possible_lengths_local); metadata.possible_lengths_local.sort(function (a, b) { return a - b; }); } // Remove duplicates. for (var _i6 = 0, _Object$keys4 = Object.keys(types); _i6 < _Object$keys4.length; _i6++) { var _type3 = _Object$keys4[_i6]; if (areEqualArrays(types[_type3].possible_lengths, metadata.possible_lengths)) { delete types[_type3].possible_lengths; } if (types[_type3].possible_lengths_local && metadata.possible_lengths_local && areEqualArrays(types[_type3].possible_lengths_local, metadata.possible_lengths_local)) { delete types[_type3].possible_lengths_local; } } } function parseXmlString(xmlString) { return new Promise(function (resolve, reject) { (0, _xml2js.parseString)(xmlString, function (error, result) { if (error) { return reject(error); } resolve(result); }); }); } //# sourceMappingURL=generate.js.map