264 lines
8.2 KiB
JavaScript
264 lines
8.2 KiB
JavaScript
"use strict";
|
|
|
|
exports.__esModule = true;
|
|
exports.validateRedirect = exports.insertParams = exports.resolve = exports.match = exports.pick = exports.startsWith = undefined;
|
|
|
|
var _invariant = require("invariant");
|
|
|
|
var _invariant2 = _interopRequireDefault(_invariant);
|
|
|
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// startsWith(string, search) - Check if `string` starts with `search`
|
|
var startsWith = function startsWith(string, search) {
|
|
return string.substr(0, search.length) === search;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// pick(routes, uri)
|
|
//
|
|
// Ranks and picks the best route to match. Each segment gets the highest
|
|
// amount of points, then the type of segment gets an additional amount of
|
|
// points where
|
|
//
|
|
// static > dynamic > splat > root
|
|
//
|
|
// This way we don't have to worry about the order of our routes, let the
|
|
// computers do it.
|
|
//
|
|
// A route looks like this
|
|
//
|
|
// { path, default, value }
|
|
//
|
|
// And a returned match looks like:
|
|
//
|
|
// { route, params, uri }
|
|
//
|
|
// I know, I should use TypeScript not comments for these types.
|
|
var pick = function pick(routes, uri) {
|
|
var match = void 0;
|
|
var default_ = void 0;
|
|
|
|
var _uri$split = uri.split("?"),
|
|
uriPathname = _uri$split[0];
|
|
|
|
var uriSegments = segmentize(uriPathname);
|
|
var isRootUri = uriSegments[0] === "";
|
|
var ranked = rankRoutes(routes);
|
|
|
|
for (var i = 0, l = ranked.length; i < l; i++) {
|
|
var missed = false;
|
|
var route = ranked[i].route;
|
|
|
|
if (route.default) {
|
|
default_ = {
|
|
route: route,
|
|
params: {},
|
|
uri: uri
|
|
};
|
|
continue;
|
|
}
|
|
|
|
var routeSegments = segmentize(route.path);
|
|
var params = {};
|
|
var max = Math.max(uriSegments.length, routeSegments.length);
|
|
var index = 0;
|
|
|
|
for (; index < max; index++) {
|
|
var routeSegment = routeSegments[index];
|
|
var uriSegment = uriSegments[index];
|
|
|
|
var _isSplat = routeSegment === "*";
|
|
if (_isSplat) {
|
|
// Hit a splat, just grab the rest, and return a match
|
|
// uri: /files/documents/work
|
|
// route: /files/*
|
|
params["*"] = uriSegments.slice(index).map(decodeURIComponent).join("/");
|
|
break;
|
|
}
|
|
|
|
if (uriSegment === undefined) {
|
|
// URI is shorter than the route, no match
|
|
// uri: /users
|
|
// route: /users/:userId
|
|
missed = true;
|
|
break;
|
|
}
|
|
|
|
var dynamicMatch = paramRe.exec(routeSegment);
|
|
|
|
if (dynamicMatch && !isRootUri) {
|
|
var matchIsNotReserved = reservedNames.indexOf(dynamicMatch[1]) === -1;
|
|
!matchIsNotReserved ? process.env.NODE_ENV !== "production" ? (0, _invariant2.default)(false, "<Router> dynamic segment \"" + dynamicMatch[1] + "\" is a reserved name. Please use a different name in path \"" + route.path + "\".") : (0, _invariant2.default)(false) : void 0;
|
|
var value = decodeURIComponent(uriSegment);
|
|
params[dynamicMatch[1]] = value;
|
|
} else if (routeSegment !== uriSegment) {
|
|
// Current segments don't match, not dynamic, not splat, so no match
|
|
// uri: /users/123/settings
|
|
// route: /users/:id/profile
|
|
missed = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!missed) {
|
|
match = {
|
|
route: route,
|
|
params: params,
|
|
uri: "/" + uriSegments.slice(0, index).join("/")
|
|
};
|
|
break;
|
|
}
|
|
}
|
|
|
|
return match || default_ || null;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// match(path, uri) - Matches just one path to a uri, also lol
|
|
var match = function match(path, uri) {
|
|
return pick([{ path: path }], uri);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// resolve(to, basepath)
|
|
//
|
|
// Resolves URIs as though every path is a directory, no files. Relative URIs
|
|
// in the browser can feel awkward because not only can you be "in a directory"
|
|
// you can be "at a file", too. For example
|
|
//
|
|
// browserSpecResolve('foo', '/bar/') => /bar/foo
|
|
// browserSpecResolve('foo', '/bar') => /foo
|
|
//
|
|
// But on the command line of a file system, it's not as complicated, you can't
|
|
// `cd` from a file, only directories. This way, links have to know less about
|
|
// their current path. To go deeper you can do this:
|
|
//
|
|
// <Link to="deeper"/>
|
|
// // instead of
|
|
// <Link to=`{${props.uri}/deeper}`/>
|
|
//
|
|
// Just like `cd`, if you want to go deeper from the command line, you do this:
|
|
//
|
|
// cd deeper
|
|
// # not
|
|
// cd $(pwd)/deeper
|
|
//
|
|
// By treating every path as a directory, linking to relative paths should
|
|
// require less contextual information and (fingers crossed) be more intuitive.
|
|
var resolve = function resolve(to, base) {
|
|
// /foo/bar, /baz/qux => /foo/bar
|
|
if (startsWith(to, "/")) {
|
|
return to;
|
|
}
|
|
|
|
var _to$split = to.split("?"),
|
|
toPathname = _to$split[0],
|
|
toQuery = _to$split[1];
|
|
|
|
var _base$split = base.split("?"),
|
|
basePathname = _base$split[0];
|
|
|
|
var toSegments = segmentize(toPathname);
|
|
var baseSegments = segmentize(basePathname);
|
|
|
|
// ?a=b, /users?b=c => /users?a=b
|
|
if (toSegments[0] === "") {
|
|
return addQuery(basePathname, toQuery);
|
|
}
|
|
|
|
// profile, /users/789 => /users/789/profile
|
|
if (!startsWith(toSegments[0], ".")) {
|
|
var pathname = baseSegments.concat(toSegments).join("/");
|
|
return addQuery((basePathname === "/" ? "" : "/") + pathname, toQuery);
|
|
}
|
|
|
|
// ./ /users/123 => /users/123
|
|
// ../ /users/123 => /users
|
|
// ../.. /users/123 => /
|
|
// ../../one /a/b/c/d => /a/b/one
|
|
// .././one /a/b/c/d => /a/b/c/one
|
|
var allSegments = baseSegments.concat(toSegments);
|
|
var segments = [];
|
|
for (var i = 0, l = allSegments.length; i < l; i++) {
|
|
var segment = allSegments[i];
|
|
if (segment === "..") segments.pop();else if (segment !== ".") segments.push(segment);
|
|
}
|
|
|
|
return addQuery("/" + segments.join("/"), toQuery);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// insertParams(path, params)
|
|
var insertParams = function insertParams(path, params) {
|
|
var segments = segmentize(path);
|
|
return "/" + segments.map(function (segment) {
|
|
var match = paramRe.exec(segment);
|
|
return match ? params[match[1]] : segment;
|
|
}).join("/");
|
|
};
|
|
|
|
var validateRedirect = function validateRedirect(from, to) {
|
|
var filter = function filter(segment) {
|
|
return isDynamic(segment);
|
|
};
|
|
var fromString = segmentize(from).filter(filter).sort().join("/");
|
|
var toString = segmentize(to).filter(filter).sort().join("/");
|
|
return fromString === toString;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// Junk
|
|
var paramRe = /^:(.+)/;
|
|
|
|
var SEGMENT_POINTS = 4;
|
|
var STATIC_POINTS = 3;
|
|
var DYNAMIC_POINTS = 2;
|
|
var SPLAT_PENALTY = 1;
|
|
var ROOT_POINTS = 1;
|
|
|
|
var isRootSegment = function isRootSegment(segment) {
|
|
return segment === "";
|
|
};
|
|
var isDynamic = function isDynamic(segment) {
|
|
return paramRe.test(segment);
|
|
};
|
|
var isSplat = function isSplat(segment) {
|
|
return segment === "*";
|
|
};
|
|
|
|
var rankRoute = function rankRoute(route, index) {
|
|
var score = route.default ? 0 : segmentize(route.path).reduce(function (score, segment) {
|
|
score += SEGMENT_POINTS;
|
|
if (isRootSegment(segment)) score += ROOT_POINTS;else if (isDynamic(segment)) score += DYNAMIC_POINTS;else if (isSplat(segment)) score -= SEGMENT_POINTS + SPLAT_PENALTY;else score += STATIC_POINTS;
|
|
return score;
|
|
}, 0);
|
|
return { route: route, score: score, index: index };
|
|
};
|
|
|
|
var rankRoutes = function rankRoutes(routes) {
|
|
return routes.map(rankRoute).sort(function (a, b) {
|
|
return a.score < b.score ? 1 : a.score > b.score ? -1 : a.index - b.index;
|
|
});
|
|
};
|
|
|
|
var segmentize = function segmentize(uri) {
|
|
return uri
|
|
// strip starting/ending slashes
|
|
.replace(/(^\/+|\/+$)/g, "").split("/");
|
|
};
|
|
|
|
var addQuery = function addQuery(pathname, query) {
|
|
return pathname + (query ? "?" + query : "");
|
|
};
|
|
|
|
var reservedNames = ["uri", "path"];
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
exports.startsWith = startsWith;
|
|
exports.pick = pick;
|
|
exports.match = match;
|
|
exports.resolve = resolve;
|
|
exports.insertParams = insertParams;
|
|
exports.validateRedirect = validateRedirect; |