Source: router/modules/path-utils/index.js

/**
 * @summary path-utilities
 * @version 3.0.0
 * @since 1.0.0
 * @author Arian Khosravi <arian.khosravi@aofl.com>
 */

/**
 * @memberof module:@aofl/router
 * @private
 * @type {RegExp}
 */
const DYNAMIC_PATH_REGEX = /:([^/\s]*)(\/?)/ig;
/**
 * @memberof module:@aofl/router
 * @private
 * @type {RegExp}
 */
const CLEAN_PATH_REGEX = /[#?].*/i;
/**
 * @memberof module:@aofl/router
 * @private
 * @type {RegExp}
 */
const TRAILING_SLASH_REGEX = /\/$/i;
/**
 * @memberof module:@aofl/router
 * @private
 * @type {RegExp}
 */
const LEADING_SLASH_REGEX = /^\//i;
/**
 * PathUtils implementation
 * @memberof module:@aofl/router
 */
class PathUtils {
  /**
   * @param {String} _path
   * @return {Object}
   */
  static getRegex(_path) {
    const path = PathUtils.removeTrailingSlash(PathUtils.cleanPath(_path));
    let regexStr = '';
    let matches = DYNAMIC_PATH_REGEX.exec(path);
    const keys = [];
    if (matches === null) {
      regexStr = path;
    } else {
      let nextMatchIndex = 0;
      while (matches) {
        regexStr += path.substring(nextMatchIndex, matches.index) + '([^\\/\\s]*)' + matches[2];
        nextMatchIndex = matches.index + matches[0].length;
        keys.push(matches[1]);
        matches = DYNAMIC_PATH_REGEX.exec(path);
      }

      regexStr += path.substr(nextMatchIndex);
    }

    const regex = new RegExp('^' + regexStr + '$');
    return {
      regex,
      parse(p) {
        if (keys.length === 0) return {};
        const cleanPath = PathUtils.removeTrailingSlash(PathUtils.cleanPath(p));
        const pathMatches = regex.exec(cleanPath);
        return keys.reduce((acc, key, index) => {
          acc[key] = pathMatches[index + 1];
          return acc;
        }, {});
      }
    };
  }


  /**
   * @param {*} path
   * @return {String}
   * @throws {Error}
   */
  static cleanPath(path) {
    return path.replace(CLEAN_PATH_REGEX, '');
  }


  /**
   * @param {String} str
   * @return {String}
   */
  static removeTrailingSlash(str) {
    if (str === '/') return str;
    return str && str.replace(TRAILING_SLASH_REGEX, '');
  }

  /**
   * @param {String} str
   * @return {String}
   */
  static removeLeadingSlash(str) {
    if (str === '/') return str;
    return str && str.replace(LEADING_SLASH_REGEX, '');
  }


  /**
   * Creates an array of url path segments from a url string
   *
   * @param {String} path
   * @return {Array}
   */
  static getPathSegments(path) {
    return path.split('/').filter((item) => item);
  }


  /**
   * Evaluates whether the given segment is dynamic
   *
   * @param {String} segment
   * @return {Boolean}
   */
  static isDynamicSegment(segment) {
    return segment.indexOf(':') > -1;
  }


  /**
   * Enumerates the number of matching segments in the given arrays of strings
   * @param {Array} segmentsA
   * @param {Array} segmentsB
   * @return {Number}
   */
  static matchingSegmentsCount(segmentsA, segmentsB) {
    let matches = 0;
    for (let i = 0; i < segmentsA.length && i < segmentsB.length; i++) {
      /* istanbul ignore else */
      if (segmentsA[i] === segmentsB[i]) {
        matches++;
      } else if (!PathUtils.isDynamicSegment(segmentsA[i]) && !PathUtils.isDynamicSegment(segmentsB[i])) {
        // Both segments are static and do not match.
        // This immediately disqualifies this as a matching route
        matches = -1;
        break;
      }
    }
    return matches;
  }
}

export default PathUtils;