react-router
Version:
A complete routing library for React.js
137 lines (108 loc) • 3.48 kB
JavaScript
import qs from 'qs';
export var parseQueryString = qs.parse;
export function stringifyQuery(query) {
return qs.stringify(query, { arrayFormat: 'brackets' });
}
var queryMatcher = /\?(.*)$/;
export function getPathname(path) {
return path.replace(queryMatcher, '');
}
export function getQueryString(path) {
var match = path.match(queryMatcher);
return match ? match[1] : '';
}
export function stripLeadingSlashes(path) {
return path ? path.replace(/^\/+/, '') : '';
}
export function isAbsolutePath(path) {
return typeof path === 'string' && path.charAt(0) === '/';
}
function escapeRegExp(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
function escapeSource(string) {
return escapeRegExp(string).replace(/\/+/g, '/+');
}
function _compilePattern(pattern) {
var regexpSource = '';
var paramNames = [];
var tokens = [];
var match, lastIndex = 0, matcher = /:([a-zA-Z_$][a-zA-Z0-9_$]*)|\*|\(|\)/g;
while (match = matcher.exec(pattern)) {
if (match.index !== lastIndex) {
tokens.push(pattern.slice(lastIndex, match.index));
regexpSource += escapeSource(pattern.slice(lastIndex, match.index));
}
if (match[1]) {
regexpSource += '([^/?#]+)';
paramNames.push(match[1]);
} else if (match[0] === '*') {
regexpSource += '(.*?)';
paramNames.push('splat');
} else if (match[0] === '(') {
regexpSource += '(?:';
} else if (match[0] === ')') {
regexpSource += ')?';
}
tokens.push(match[0]);
lastIndex = matcher.lastIndex;
}
if (lastIndex !== pattern.length) {
tokens.push(pattern.slice(lastIndex, pattern.length));
regexpSource += escapeSource(pattern.slice(lastIndex, pattern.length));
}
return {
pattern,
regexpSource,
paramNames,
tokens
};
}
var CompiledPatternsCache = {};
export function compilePattern(pattern) {
if (!(pattern in CompiledPatternsCache))
CompiledPatternsCache[pattern] = _compilePattern(pattern);
return CompiledPatternsCache[pattern];
}
/**
* Attempts to match a pattern on the given pathname. Patterns may use
* the following special characters:
*
* - :paramName Matches a URL segment up to the next /, ?, or #. The
* captured string is considered a "param"
* - () Wraps a segment of the URL that is optional
* - * Consumes (non-greedy) all characters up to the next
* character in the pattern, or to the end of the URL if
* there is none
*
* The return value is an object with the following properties:
*
* - remainingPathname
* - paramNames
* - paramValues
*/
export function matchPattern(pattern, pathname) {
var { regexpSource, paramNames, tokens } = compilePattern(stripLeadingSlashes(pattern));
regexpSource += '/*'; // Ignore trailing slashes
var captureRemaining = tokens[tokens.length - 1] !== '*';
if (captureRemaining)
regexpSource += '(.*?)';
var match = pathname.match(new RegExp('^' + regexpSource + '$', 'i'));
var remainingPathname, paramValues;
if (match != null) {
paramValues = Array.prototype.slice.call(match, 1);
if (captureRemaining) {
remainingPathname = paramValues.pop();
} else {
remainingPathname = pathname.replace(match[0], '');
}
}
return {
remainingPathname,
paramNames,
paramValues
};
}
export function getParamNames(pattern) {
return compilePattern(pattern).paramNames;
}