UNPKG

react-router

Version:

A complete routing library for React.js

148 lines (112 loc) 3.76 kB
var invariant = require('react/lib/invariant'); var qs = require('querystring'); var mergeProperties = require('./mergeProperties'); var URL = require('./URL'); var paramMatcher = /((?::[a-z_$][a-z0-9_$]*)|\*)/ig; var queryMatcher = /\?(.+)/; function getParamName(pathSegment) { return pathSegment === '*' ? 'splat' : pathSegment.substr(1); } var _compiledPatterns = {}; function compilePattern(pattern) { if (_compiledPatterns[pattern]) return _compiledPatterns[pattern]; var compiled = _compiledPatterns[pattern] = {}; var paramNames = compiled.paramNames = []; var source = pattern.replace(paramMatcher, function (match, pathSegment) { paramNames.push(getParamName(pathSegment)); return pathSegment === '*' ? '(.*?)' : '([^/?#]+)'; }); compiled.matcher = new RegExp('^' + source + '$', 'i'); return compiled; } function isDynamicPattern(pattern) { return pattern.indexOf(':') !== -1 || pattern.indexOf('*') !== -1; } var Path = { /** * 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) { if (!pattern) return null; if (!isDynamicPattern(pattern)) { if (pattern === URL.decode(path)) return {}; // No dynamic segments, but the paths match. return null; } var compiled = compilePattern(pattern); var match = URL.decode(path).match(compiled.matcher); if (!match) return null; var params = {}; compiled.paramNames.forEach(function (paramName, index) { params[paramName] = match[index + 1]; }); return params; }, /** * Returns an array of the names of all parameters in the given pattern. */ extractParamNames: function (pattern) { if (!pattern) return []; return compilePattern(pattern).paramNames; }, /** * 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) { if (!pattern) return null; if (!isDynamicPattern(pattern)) return pattern; params = params || {}; return pattern.replace(paramMatcher, function (match, pathSegment) { var paramName = getParamName(pathSegment); invariant( params[paramName] != null, 'Missing "' + paramName + '" parameter for path "' + pattern + '"' ); // Preserve forward slashes. return String(params[paramName]).split('/').map(URL.encode).join('/'); }); }, /** * 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 = 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 * added to the query string. */ withQuery: function (path, query) { var existingQuery = Path.extractQuery(path); if (existingQuery) query = query ? mergeProperties(existingQuery, query) : existingQuery; var queryString = query && qs.stringify(query); if (queryString) return Path.withoutQuery(path) + '?' + queryString; return path; }, /** * Returns a normalized version of the given path. */ normalize: function (path) { return path.replace(/^\/*/, '/'); } }; module.exports = Path;