UNPKG

react-router

Version:
167 lines (132 loc) 4.09 kB
var invariant = require('react/lib/invariant'); var merge = require('qs/lib/utils').merge; var qs = require('qs'); function encodeURL(url) { return encodeURIComponent(url).replace(/%20/g, '+'); } function decodeURL(url) { return decodeURIComponent(url.replace(/\+/g, ' ')); } function encodeURLPath(path) { return String(path).split('/').map(encodeURL).join('/'); } var paramMatcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|[*.()\[\]\\+|{}^$]/g; var queryMatcher = /\?(.+)/; var _compiledPatterns = {}; function compilePattern(pattern) { if (!(pattern in _compiledPatterns)) { var paramNames = []; var source = pattern.replace(paramMatcher, 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 Path = { /** * Returns an array of the names of all parameters in the given pattern. */ extractParamNames: function (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 (pattern, path) { var object = compilePattern(pattern); var match = decodeURL(path).match(object.matcher); if (!match) return null; var params = {}; object.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 (pattern, params) { params = params || {}; var splatIndex = 0; return pattern.replace(paramMatcher, function (match, paramName) { paramName = paramName || 'splat'; invariant( params[paramName] != null, 'Missing "' + paramName + '" parameter for path "' + pattern + '"' ); var segment; if (paramName === 'splat' && Array.isArray(params[paramName])) { segment = params[paramName][splatIndex++]; invariant( segment != null, 'Missing splat # ' + splatIndex + ' for path "' + pattern + '"' ); } else { segment = params[paramName]; } return encodeURLPath(segment); }); }, /** * 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 (path) { var match = decodeURL(path).match(queryMatcher); return match && qs.parse(match[1]); }, /** * Returns a version of the given path without the query string. */ withoutQuery: function (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 (path, query) { var existingQuery = Path.extractQuery(path); if (existingQuery) query = query ? merge(existingQuery, query) : existingQuery; var queryString = query && qs.stringify(query); if (queryString) return Path.withoutQuery(path) + '?' + queryString; return path; }, /** * Returns true if the given path is absolute. */ isAbsolute: function (path) { return path.charAt(0) === '/'; }, /** * Returns a normalized version of the given path. */ normalize: function (path, parentRoute) { return path.replace(/^\/*/, '/'); }, /** * Joins two URL paths together. */ join: function (a, b) { return a.replace(/\/*$/, '/') + b; } }; module.exports = Path;