"use strict"; var invariant = require("react/lib/invariant"); var objectAssign = require("object-assign"); var qs = require("qs"); var paramCompileMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g; var paramInjectMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$?]*[?]?)|[*]/g; var paramInjectTrailingSlashMatcher = /\/\/\?|\/\?\/|\/\?/g; var queryMatcher = /\?(.*)$/; var _compiledPatterns = {}; function compilePattern(pattern) { if (!(pattern in _compiledPatterns)) { var paramNames = []; var source = pattern.replace(paramCompileMatcher, function (match, paramName) { if (paramName) { paramNames.push(paramName); return "([^/?#]+)"; } else if (match === "*") { paramNames.push("splat"); return "(.*?)"; } else { return "\\" + match; } }); _compiledPatterns[pattern] = { matcher: new RegExp("^" + source + "$", "i"), paramNames: paramNames }; } return _compiledPatterns[pattern]; } var PathUtils = { /** * Returns true if the given path is absolute. */ isAbsolute: function isAbsolute(path) { return path.charAt(0) === "/"; }, /** * Joins two URL paths together. */ join: function join(a, b) { return a.replace(/\/*$/, "/") + b; }, /** * Returns an array of the names of all parameters in the given pattern. */ extractParamNames: function extractParamNames(pattern) { return compilePattern(pattern).paramNames; }, /** * Extracts the portions of the given URL path that match the given pattern * and returns an object of param name => value pairs. Returns null if the * pattern does not match the given path. */ extractParams: function extractParams(pattern, path) { var _compilePattern = compilePattern(pattern); var matcher = _compilePattern.matcher; var paramNames = _compilePattern.paramNames; var match = path.match(matcher); if (!match) { return null; }var params = {}; paramNames.forEach(function (paramName, index) { params[paramName] = match[index + 1]; }); return params; }, /** * Returns a version of the given route path with params interpolated. Throws * if there is a dynamic segment of the route path for which there is no param. */ injectParams: function injectParams(pattern, params) { params = params || {}; var splatIndex = 0; return pattern.replace(paramInjectMatcher, function (match, paramName) { paramName = paramName || "splat"; // If param is optional don't check for existence if (paramName.slice(-1) === "?") { paramName = paramName.slice(0, -1); if (params[paramName] == null) return ""; } else { invariant(params[paramName] != null, "Missing \"%s\" parameter for path \"%s\"", paramName, pattern); } var segment; if (paramName === "splat" && Array.isArray(params[paramName])) { segment = params[paramName][splatIndex++]; invariant(segment != null, "Missing splat # %s for path \"%s\"", splatIndex, pattern); } else { segment = params[paramName]; } return segment; }).replace(paramInjectTrailingSlashMatcher, "/"); }, /** * Returns an object that is the result of parsing any query string contained * in the given path, null if the path contains no query string. */ extractQuery: function extractQuery(path) { var match = path.match(queryMatcher); return match && qs.parse(match[1]); }, /** * Returns a version of the given path without the query string. */ withoutQuery: function withoutQuery(path) { return path.replace(queryMatcher, ""); }, /** * Returns a version of the given path with the parameters in the given * query merged into the query string. */ withQuery: function withQuery(path, query) { var existingQuery = PathUtils.extractQuery(path); if (existingQuery) query = query ? objectAssign(existingQuery, query) : existingQuery; var queryString = qs.stringify(query, { arrayFormat: "brackets" }); if (queryString) { return PathUtils.withoutQuery(path) + "?" + queryString; }return PathUtils.withoutQuery(path); } }; module.exports = PathUtils;