@curi/core
Advanced tools
Comparing version 1.0.0-beta.28 to 1.0.0-beta.29
@@ -1,3 +0,11 @@ | ||
## 1.0.0-beta.27 | ||
## 1.0.0-beta.29 | ||
* `route.response()` returns object with properties to update and removes `set` from the properties passed to the function. `redirectTo` will auto-generate the location from the provided `name` and `params` while all of the other properties will be copied directly. | ||
* Do no emit a response if no routes match. The user should add a catch-all route (`{ path: "(.*)" }`) to handle these themselves. | ||
* Move `match.response` to top level and group `match.every` and `match.initial` under `on` object (`on.initial` and `on.every`). | ||
* Group `initial` and `every` resolved values with `error` under `resolved` object. | ||
* Rename `router.refresh` to `router.replaceRoutes`. | ||
## 1.0.0-beta.28 | ||
* If no route has a `match.initial` or `match.every` function, the router will emit in synchronous mode. In synchronous mode, `router.respond` is not necessary for delaying the initial render. | ||
@@ -4,0 +12,0 @@ |
@@ -21,6 +21,3 @@ 'use strict'; | ||
***************************************************************************** */ | ||
/* global Reflect, Promise */ | ||
var __assign = Object.assign || function __assign(t) { | ||
@@ -34,6 +31,16 @@ for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
function registerRoutes(routes, addon, parentData) { | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) | ||
t[p[i]] = s[p[i]]; | ||
return t; | ||
} | ||
function registerRoutes(routes, interaction, parentData) { | ||
routes.forEach(function (route) { | ||
var data = addon.register(route.public, parentData); | ||
registerRoutes(route.children, addon, data); | ||
var data = interaction.register(route.public, parentData); | ||
registerRoutes(route.children, interaction, data); | ||
}); | ||
@@ -55,3 +62,3 @@ } | ||
function createPathnameAddon(options) { | ||
function generatePathname(options) { | ||
var knownPaths = {}; | ||
@@ -94,93 +101,43 @@ var cache = {}; | ||
function routeProperties(route, props) { | ||
return { | ||
params: props.params, | ||
location: props.location, | ||
name: route.public.name | ||
}; | ||
function createRedirect(redirectTo, interactions) { | ||
var name = redirectTo.name, params = redirectTo.params, rest = __rest(redirectTo, ["name", "params"]); | ||
var pathname = interactions.pathname(name, params); | ||
return __assign({ pathname: pathname }, rest); | ||
} | ||
function responseSetters(props, addons) { | ||
return { | ||
redirect: function (redProps) { | ||
var name = redProps.name, params = redProps.params, query = redProps.query, hash = redProps.hash, state = redProps.state, _a = redProps.status, status = _a === void 0 ? 301 : _a; | ||
props.status = status; | ||
var pathname = addons.pathname(name, params); | ||
props.redirectTo = { | ||
pathname: pathname, | ||
query: query, | ||
hash: hash, | ||
state: state | ||
}; | ||
}, | ||
error: function (err) { | ||
props.error = err; | ||
}, | ||
status: function (code) { | ||
props.status = code; | ||
}, | ||
data: function (data) { | ||
props.data = data; | ||
}, | ||
body: function (body) { | ||
props.body = body; | ||
}, | ||
title: function (title) { | ||
props.title = title; | ||
} | ||
}; | ||
} | ||
function freezeResponse(route, props) { | ||
var response = Object.assign({ | ||
key: props.location.key, | ||
name: route ? route.public.name : undefined | ||
}, props); | ||
return response; | ||
} | ||
function finishResponse(pending, addons) { | ||
var error = pending.error, resolved = pending.resolved, route = pending.route, props = pending.props; | ||
if (route && route.public.match.response) { | ||
route.public.match.response({ | ||
error: error, | ||
resolved: resolved, | ||
route: routeProperties(route, props), | ||
set: responseSetters(props, addons), | ||
addons: addons | ||
}); | ||
function finishResponse(match, interactions, resolved) { | ||
var route = match.route, response = match.response; | ||
if (!route.response) { | ||
return response; | ||
} | ||
return freezeResponse(route, props); | ||
} | ||
function matchRoute(route, pathname, matches, parentPath) { | ||
var testPath = stripLeadingSlash(pathname); | ||
var _a = route.match, re = _a.re, keys = _a.keys, mustBeExact = _a.mustBeExact; | ||
var children = route.children; | ||
var match = re.exec(testPath); | ||
if (!match) { | ||
return false; | ||
} | ||
var segment = match[0], parsed = match.slice(1); | ||
var params = {}; | ||
keys.forEach(function (key, index) { | ||
params[key.name] = parsed[index]; | ||
var responseModifiers = route.response({ | ||
resolved: resolved, | ||
name: response.name, | ||
params: __assign({}, response.params), | ||
location: response.location, | ||
route: interactions | ||
}); | ||
var uriString = parentPath != null ? join(parentPath, segment) : withLeadingSlash(segment); | ||
matches.push({ route: route, params: params }); | ||
// if there are no children, then we accept the match | ||
if (!children || !children.length) { | ||
return true; | ||
if (!responseModifiers) { | ||
return response; | ||
} | ||
// children only need to match against unmatched segments | ||
var remainder = testPath.slice(segment.length); | ||
var notExact = !!remainder.length; | ||
var hasChildMatch = children.some(function (c) { | ||
return matchRoute(c, remainder, matches, uriString); | ||
var settableProperties = [ | ||
"status", | ||
"error", | ||
"body", | ||
"data", | ||
"title", | ||
"redirectTo" | ||
]; | ||
// only merge the valid properties onto the response | ||
settableProperties.forEach(function (p) { | ||
if (responseModifiers.hasOwnProperty(p)) { | ||
if (p === "redirectTo") { | ||
// special case | ||
response[p] = createRedirect(responseModifiers[p], interactions); | ||
} | ||
else { | ||
response[p] = responseModifiers[p]; | ||
} | ||
} | ||
}); | ||
// if the route has children, but none of them match, remove the match unless it | ||
// is exact | ||
if (mustBeExact && notExact && !hasChildMatch) { | ||
matches.pop(); | ||
return false; | ||
} | ||
return true; | ||
return response; | ||
} | ||
@@ -212,74 +169,99 @@ | ||
function matchLocation(location, routes) { | ||
var matches = []; | ||
function matchRoute(route, pathname) { | ||
var testPath = stripLeadingSlash(pathname); | ||
var regExpMatch = route.pathMatching.re.exec(testPath); | ||
if (!regExpMatch) { | ||
return []; | ||
} | ||
var matchedSegment = regExpMatch[0], parsed = regExpMatch.slice(1); | ||
var params = {}; | ||
route.pathMatching.keys.forEach(function (key, index) { | ||
params[key.name] = parsed[index]; | ||
}); | ||
var matches = [{ route: route, params: params }]; | ||
// if there are no children routes, immediately accept the match | ||
if (!route.children.length) { | ||
return matches; | ||
} | ||
// children only need to match against unmatched segments | ||
var remainingSegments = testPath.slice(matchedSegment.length); | ||
var childrenLength = route.children.length; | ||
for (var i = 0; i < childrenLength; i++) { | ||
var matched = matchRoute(route.children[i], remainingSegments); | ||
if (matched.length) { | ||
matches = matches.concat(matched); | ||
break; | ||
} | ||
} | ||
// return matches if a child route matches or this route matches exactly | ||
return matches.length > 1 || | ||
(route.pathMatching.mustBeExact && remainingSegments.length === 0) | ||
? matches | ||
: []; | ||
} | ||
function createMatch(routeMatches, location) { | ||
var partials = []; | ||
var params = {}; | ||
var route; | ||
var bestMatch = routeMatches.pop(); | ||
// handle ancestor routes | ||
routeMatches.forEach(function (match) { | ||
partials.push(match.route.public.name); | ||
Object.assign(params, parseParams(match.params, match.route.paramParsers)); | ||
}); | ||
// handle best match | ||
Object.assign(params, parseParams(bestMatch.params, bestMatch.route.paramParsers)); | ||
return { | ||
route: bestMatch.route, | ||
response: { | ||
location: location, | ||
key: location.key, | ||
name: bestMatch.route.public.name, | ||
params: params, | ||
partials: partials, | ||
status: 200, | ||
body: undefined, | ||
data: undefined, | ||
title: "" | ||
} | ||
}; | ||
} | ||
function matchLocation(location, routes) { | ||
// determine which route(s) match, then use the exact match | ||
// as the matched route and the rest as partial routes | ||
routes.some(function (route) { return matchRoute(route, location.pathname, matches); }); | ||
if (matches.length) { | ||
var bestMatch = matches.pop(); | ||
matches.forEach(function (m) { | ||
partials.push(m.route.public.name); | ||
Object.assign(params, parseParams(m.params, m.route.paramParsers)); | ||
}); | ||
route = bestMatch.route; | ||
Object.assign(params, parseParams(bestMatch.params, route.paramParsers)); | ||
var routeLength = routes.length; | ||
for (var i = 0; i < routeLength; i++) { | ||
var routeMatches = matchRoute(routes[i], location.pathname); | ||
if (routeMatches.length) { | ||
return createMatch(routeMatches, location); | ||
} | ||
} | ||
// start building the properties of the response object | ||
var props = { | ||
location: location, | ||
params: params, | ||
partials: partials, | ||
status: route != null ? 200 : 404, | ||
body: undefined, | ||
data: undefined, | ||
title: "" | ||
// no matching route | ||
return { | ||
route: undefined, | ||
response: undefined | ||
}; | ||
return { route: route, props: props }; | ||
} | ||
function createResponse(location, routes) { | ||
return __assign({}, matchLocation(location, routes), { resolved: null, error: null }); | ||
} | ||
function asyncCreateResponse(location, routes) { | ||
var _a = matchLocation(location, routes), route = _a.route, props = _a.props; | ||
return loadRoute(route, props); | ||
} | ||
/* | ||
* This will call any initial/every match functions for the matching route | ||
*/ | ||
function loadRoute(route, props) { | ||
if (!route) { | ||
return Promise.resolve({ | ||
route: route, | ||
props: props | ||
}); | ||
} | ||
var match = route.public.match; | ||
function resolveRoute(match) { | ||
var response = match.response; | ||
var on = match.route.public.on; | ||
return Promise.all([ | ||
match.initial ? match.initial() : undefined, | ||
match.every ? match.every(routeProperties(route, props)) : undefined | ||
on.initial && on.initial(), | ||
on.every && | ||
on.every({ | ||
name: response.name, | ||
params: __assign({}, response.params), | ||
location: response.location | ||
}) | ||
]).then(function (_a) { | ||
var initial = _a[0], every = _a[1]; | ||
var resolved = !match.initial && !match.every ? null : { initial: initial, every: every }; | ||
return { | ||
route: route, | ||
props: props, | ||
error: null, | ||
resolved: resolved | ||
}; | ||
}, function (err) { | ||
// when there is an uncaught error, set it on the response | ||
return { | ||
route: route, | ||
props: props, | ||
error: err, | ||
resolved: null | ||
}; | ||
}); | ||
return ({ error: null, initial: initial, every: every }); | ||
}, function (error) { return ({ error: error, initial: null, every: null }); }); | ||
} | ||
function once(fn) { | ||
var promise = null; | ||
var promise; | ||
var hasRun = false; | ||
@@ -297,4 +279,5 @@ return function () { | ||
var createRoute = function (options) { | ||
var name = options.name, path = options.path, _a = options.pathOptions, pathOptions = _a === void 0 ? {} : _a, _b = options.children, descriptorChildren = _b === void 0 ? [] : _b, _c = options.match, match = _c === void 0 ? {} : _c, extra = options.extra, paramParsers = options.params; | ||
var name = options.name, path = options.path, _a = options.pathOptions, pathOptions = _a === void 0 ? {} : _a, _b = options.children, descriptorChildren = _b === void 0 ? [] : _b, response = options.response, _c = options.on, on = _c === void 0 ? {} : _c, extra = options.extra, paramParsers = options.params; | ||
// end defaults to true, so end has to be hardcoded for it to be false | ||
// set this before setting pathOptions.end for children | ||
var mustBeExact = pathOptions.end == null || pathOptions.end; | ||
@@ -308,2 +291,3 @@ var children = []; | ||
} | ||
// keys is populated by PathToRegexp | ||
var keys = []; | ||
@@ -316,10 +300,9 @@ var re = PathToRegexp(path, keys, pathOptions); | ||
keys: keys.map(function (key) { return key.name; }), | ||
match: { | ||
initial: match.initial && once(match.initial), | ||
every: match.every, | ||
response: match.response | ||
on: { | ||
initial: on.initial && once(on.initial), | ||
every: on.every | ||
}, | ||
extra: extra | ||
}, | ||
match: { | ||
pathMatching: { | ||
re: re, | ||
@@ -329,2 +312,3 @@ keys: keys, | ||
}, | ||
response: response, | ||
children: children, | ||
@@ -335,9 +319,9 @@ paramParsers: paramParsers | ||
function hasMatchFunction(route) { | ||
var match = route.public.match; | ||
return !!(match && (match.every || match.initial)); | ||
function hasAsyncOnFunction(route) { | ||
var on = route.public.on; | ||
return !!(on && (on.every || on.initial)); | ||
} | ||
function hasAsyncRoute(routes) { | ||
return routes.some(function (route) { | ||
if (hasMatchFunction(route)) { | ||
if (hasAsyncOnFunction(route)) { | ||
return true; | ||
@@ -354,3 +338,3 @@ } | ||
if (options === void 0) { options = {}; } | ||
var _a = options, _b = _a.addons, userAddons = _b === void 0 ? [] : _b, _c = _a.sideEffects, sideEffects = _c === void 0 ? [] : _c, cache = _a.cache, pathnameOptions = _a.pathnameOptions, _d = _a.emitRedirects, emitRedirects = _d === void 0 ? true : _d; | ||
var _a = options.route, userInteractions = _a === void 0 ? [] : _a, _b = options.sideEffects, sideEffects = _b === void 0 ? [] : _b, cache = options.cache, pathnameOptions = options.pathnameOptions, _c = options.emitRedirects, emitRedirects = _c === void 0 ? true : _c; | ||
var sync = true; | ||
@@ -368,15 +352,15 @@ var beforeSideEffects = []; | ||
var routes = []; | ||
var registeredAddons = {}; | ||
// add the pathname addon to the provided addons | ||
var allAddons = userAddons.concat(createPathnameAddon(pathnameOptions)); | ||
function setupRoutesAndAddons(routeArray) { | ||
var registeredInteractions = {}; | ||
// add the pathname interaction to the provided interactions | ||
var allInteractions = userInteractions.concat(generatePathname(pathnameOptions)); | ||
function setupRoutesAndInteractions(routeArray) { | ||
routes = routeArray.map(createRoute); | ||
sync = !hasAsyncRoute(routes); | ||
for (var key in registeredAddons) { | ||
delete registeredAddons[key]; | ||
for (var key in registeredInteractions) { | ||
delete registeredInteractions[key]; | ||
} | ||
allAddons.forEach(function (addon) { | ||
addon.reset(); | ||
registeredAddons[addon.name] = addon.get; | ||
registerRoutes(routes, addon); | ||
allInteractions.forEach(function (interaction) { | ||
interaction.reset(); | ||
registeredInteractions[interaction.name] = interaction.get; | ||
registerRoutes(routes, interaction); | ||
}); | ||
@@ -411,2 +395,5 @@ } | ||
function emit(response, navigation) { | ||
// store for current() and respond() | ||
mostRecent.response = response; | ||
mostRecent.navigation = navigation; | ||
var resp = { response: response, navigation: navigation, router: router }; | ||
@@ -425,3 +412,5 @@ beforeSideEffects.forEach(function (fn) { | ||
var fn = oneTimers.pop(); | ||
fn(resp); | ||
if (fn) { | ||
fn(resp); | ||
} | ||
} | ||
@@ -447,12 +436,21 @@ afterSideEffects.forEach(function (fn) { | ||
cacheAndEmit(cachedResponse, navigation); | ||
return; | ||
} | ||
} | ||
var match = matchLocation(pendingNav.location, routes); | ||
// if no routes match, do nothing | ||
if (!match.route) { | ||
console.error("The current location (" + pendingNav.location.pathname + ") has no matching route, " + | ||
'so a response could not be generated. A catch-all route ({ path: "(.*)" }) ' + | ||
"can be used to match locations with no other matching route."); | ||
pendingNav.finish(); | ||
return; | ||
} | ||
if (sync) { | ||
var pendingResponse = createResponse(pendingNav.location, routes); | ||
pendingNav.finish(); | ||
var response = finishResponse(pendingResponse, registeredAddons); | ||
var response = finishResponse(match, registeredInteractions, null); | ||
cacheAndEmit(response, navigation); | ||
} | ||
else { | ||
asyncCreateResponse(pendingNav.location, routes).then(function (pendingResponse) { | ||
resolveRoute(match).then(function (resolved) { | ||
if (pendingNav.cancelled) { | ||
@@ -462,3 +460,3 @@ return; | ||
pendingNav.finish(); | ||
var response = finishResponse(pendingResponse, registeredAddons); | ||
var response = finishResponse(match, registeredInteractions, resolved); | ||
cacheAndEmit(response, navigation); | ||
@@ -474,7 +472,5 @@ }); | ||
if (!response.redirectTo || emitRedirects) { | ||
mostRecent.response = response; | ||
mostRecent.navigation = navigation; | ||
emit(response, navigation); | ||
} | ||
if (response.redirectTo) { | ||
if (response.redirectTo !== undefined) { | ||
history.replace(response.redirectTo); | ||
@@ -484,9 +480,9 @@ } | ||
// now that everything is defined, actually do the setup | ||
setupRoutesAndAddons(routeArray); | ||
setupRoutesAndInteractions(routeArray); | ||
history.respondWith(navigationHandler); | ||
var router = { | ||
addons: registeredAddons, | ||
route: registeredInteractions, | ||
history: history, | ||
respond: respond, | ||
refresh: setupRoutesAndAddons, | ||
replaceRoutes: setupRoutesAndInteractions, | ||
current: function () { | ||
@@ -493,0 +489,0 @@ return { |
@@ -17,6 +17,3 @@ import PathToRegexp from 'path-to-regexp'; | ||
***************************************************************************** */ | ||
/* global Reflect, Promise */ | ||
var __assign = Object.assign || function __assign(t) { | ||
@@ -30,6 +27,16 @@ for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
function registerRoutes(routes, addon, parentData) { | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) | ||
t[p[i]] = s[p[i]]; | ||
return t; | ||
} | ||
function registerRoutes(routes, interaction, parentData) { | ||
routes.forEach(function (route) { | ||
var data = addon.register(route.public, parentData); | ||
registerRoutes(route.children, addon, data); | ||
var data = interaction.register(route.public, parentData); | ||
registerRoutes(route.children, interaction, data); | ||
}); | ||
@@ -51,3 +58,3 @@ } | ||
function createPathnameAddon(options) { | ||
function generatePathname(options) { | ||
var knownPaths = {}; | ||
@@ -90,93 +97,43 @@ var cache = {}; | ||
function routeProperties(route, props) { | ||
return { | ||
params: props.params, | ||
location: props.location, | ||
name: route.public.name | ||
}; | ||
function createRedirect(redirectTo, interactions) { | ||
var name = redirectTo.name, params = redirectTo.params, rest = __rest(redirectTo, ["name", "params"]); | ||
var pathname = interactions.pathname(name, params); | ||
return __assign({ pathname: pathname }, rest); | ||
} | ||
function responseSetters(props, addons) { | ||
return { | ||
redirect: function (redProps) { | ||
var name = redProps.name, params = redProps.params, query = redProps.query, hash = redProps.hash, state = redProps.state, _a = redProps.status, status = _a === void 0 ? 301 : _a; | ||
props.status = status; | ||
var pathname = addons.pathname(name, params); | ||
props.redirectTo = { | ||
pathname: pathname, | ||
query: query, | ||
hash: hash, | ||
state: state | ||
}; | ||
}, | ||
error: function (err) { | ||
props.error = err; | ||
}, | ||
status: function (code) { | ||
props.status = code; | ||
}, | ||
data: function (data) { | ||
props.data = data; | ||
}, | ||
body: function (body) { | ||
props.body = body; | ||
}, | ||
title: function (title) { | ||
props.title = title; | ||
} | ||
}; | ||
} | ||
function freezeResponse(route, props) { | ||
var response = Object.assign({ | ||
key: props.location.key, | ||
name: route ? route.public.name : undefined | ||
}, props); | ||
return response; | ||
} | ||
function finishResponse(pending, addons) { | ||
var error = pending.error, resolved = pending.resolved, route = pending.route, props = pending.props; | ||
if (route && route.public.match.response) { | ||
route.public.match.response({ | ||
error: error, | ||
resolved: resolved, | ||
route: routeProperties(route, props), | ||
set: responseSetters(props, addons), | ||
addons: addons | ||
}); | ||
function finishResponse(match, interactions, resolved) { | ||
var route = match.route, response = match.response; | ||
if (!route.response) { | ||
return response; | ||
} | ||
return freezeResponse(route, props); | ||
} | ||
function matchRoute(route, pathname, matches, parentPath) { | ||
var testPath = stripLeadingSlash(pathname); | ||
var _a = route.match, re = _a.re, keys = _a.keys, mustBeExact = _a.mustBeExact; | ||
var children = route.children; | ||
var match = re.exec(testPath); | ||
if (!match) { | ||
return false; | ||
} | ||
var segment = match[0], parsed = match.slice(1); | ||
var params = {}; | ||
keys.forEach(function (key, index) { | ||
params[key.name] = parsed[index]; | ||
var responseModifiers = route.response({ | ||
resolved: resolved, | ||
name: response.name, | ||
params: __assign({}, response.params), | ||
location: response.location, | ||
route: interactions | ||
}); | ||
var uriString = parentPath != null ? join(parentPath, segment) : withLeadingSlash(segment); | ||
matches.push({ route: route, params: params }); | ||
// if there are no children, then we accept the match | ||
if (!children || !children.length) { | ||
return true; | ||
if (!responseModifiers) { | ||
return response; | ||
} | ||
// children only need to match against unmatched segments | ||
var remainder = testPath.slice(segment.length); | ||
var notExact = !!remainder.length; | ||
var hasChildMatch = children.some(function (c) { | ||
return matchRoute(c, remainder, matches, uriString); | ||
var settableProperties = [ | ||
"status", | ||
"error", | ||
"body", | ||
"data", | ||
"title", | ||
"redirectTo" | ||
]; | ||
// only merge the valid properties onto the response | ||
settableProperties.forEach(function (p) { | ||
if (responseModifiers.hasOwnProperty(p)) { | ||
if (p === "redirectTo") { | ||
// special case | ||
response[p] = createRedirect(responseModifiers[p], interactions); | ||
} | ||
else { | ||
response[p] = responseModifiers[p]; | ||
} | ||
} | ||
}); | ||
// if the route has children, but none of them match, remove the match unless it | ||
// is exact | ||
if (mustBeExact && notExact && !hasChildMatch) { | ||
matches.pop(); | ||
return false; | ||
} | ||
return true; | ||
return response; | ||
} | ||
@@ -208,74 +165,99 @@ | ||
function matchLocation(location, routes) { | ||
var matches = []; | ||
function matchRoute(route, pathname) { | ||
var testPath = stripLeadingSlash(pathname); | ||
var regExpMatch = route.pathMatching.re.exec(testPath); | ||
if (!regExpMatch) { | ||
return []; | ||
} | ||
var matchedSegment = regExpMatch[0], parsed = regExpMatch.slice(1); | ||
var params = {}; | ||
route.pathMatching.keys.forEach(function (key, index) { | ||
params[key.name] = parsed[index]; | ||
}); | ||
var matches = [{ route: route, params: params }]; | ||
// if there are no children routes, immediately accept the match | ||
if (!route.children.length) { | ||
return matches; | ||
} | ||
// children only need to match against unmatched segments | ||
var remainingSegments = testPath.slice(matchedSegment.length); | ||
var childrenLength = route.children.length; | ||
for (var i = 0; i < childrenLength; i++) { | ||
var matched = matchRoute(route.children[i], remainingSegments); | ||
if (matched.length) { | ||
matches = matches.concat(matched); | ||
break; | ||
} | ||
} | ||
// return matches if a child route matches or this route matches exactly | ||
return matches.length > 1 || | ||
(route.pathMatching.mustBeExact && remainingSegments.length === 0) | ||
? matches | ||
: []; | ||
} | ||
function createMatch(routeMatches, location) { | ||
var partials = []; | ||
var params = {}; | ||
var route; | ||
var bestMatch = routeMatches.pop(); | ||
// handle ancestor routes | ||
routeMatches.forEach(function (match) { | ||
partials.push(match.route.public.name); | ||
Object.assign(params, parseParams(match.params, match.route.paramParsers)); | ||
}); | ||
// handle best match | ||
Object.assign(params, parseParams(bestMatch.params, bestMatch.route.paramParsers)); | ||
return { | ||
route: bestMatch.route, | ||
response: { | ||
location: location, | ||
key: location.key, | ||
name: bestMatch.route.public.name, | ||
params: params, | ||
partials: partials, | ||
status: 200, | ||
body: undefined, | ||
data: undefined, | ||
title: "" | ||
} | ||
}; | ||
} | ||
function matchLocation(location, routes) { | ||
// determine which route(s) match, then use the exact match | ||
// as the matched route and the rest as partial routes | ||
routes.some(function (route) { return matchRoute(route, location.pathname, matches); }); | ||
if (matches.length) { | ||
var bestMatch = matches.pop(); | ||
matches.forEach(function (m) { | ||
partials.push(m.route.public.name); | ||
Object.assign(params, parseParams(m.params, m.route.paramParsers)); | ||
}); | ||
route = bestMatch.route; | ||
Object.assign(params, parseParams(bestMatch.params, route.paramParsers)); | ||
var routeLength = routes.length; | ||
for (var i = 0; i < routeLength; i++) { | ||
var routeMatches = matchRoute(routes[i], location.pathname); | ||
if (routeMatches.length) { | ||
return createMatch(routeMatches, location); | ||
} | ||
} | ||
// start building the properties of the response object | ||
var props = { | ||
location: location, | ||
params: params, | ||
partials: partials, | ||
status: route != null ? 200 : 404, | ||
body: undefined, | ||
data: undefined, | ||
title: "" | ||
// no matching route | ||
return { | ||
route: undefined, | ||
response: undefined | ||
}; | ||
return { route: route, props: props }; | ||
} | ||
function createResponse(location, routes) { | ||
return __assign({}, matchLocation(location, routes), { resolved: null, error: null }); | ||
} | ||
function asyncCreateResponse(location, routes) { | ||
var _a = matchLocation(location, routes), route = _a.route, props = _a.props; | ||
return loadRoute(route, props); | ||
} | ||
/* | ||
* This will call any initial/every match functions for the matching route | ||
*/ | ||
function loadRoute(route, props) { | ||
if (!route) { | ||
return Promise.resolve({ | ||
route: route, | ||
props: props | ||
}); | ||
} | ||
var match = route.public.match; | ||
function resolveRoute(match) { | ||
var response = match.response; | ||
var on = match.route.public.on; | ||
return Promise.all([ | ||
match.initial ? match.initial() : undefined, | ||
match.every ? match.every(routeProperties(route, props)) : undefined | ||
on.initial && on.initial(), | ||
on.every && | ||
on.every({ | ||
name: response.name, | ||
params: __assign({}, response.params), | ||
location: response.location | ||
}) | ||
]).then(function (_a) { | ||
var initial = _a[0], every = _a[1]; | ||
var resolved = !match.initial && !match.every ? null : { initial: initial, every: every }; | ||
return { | ||
route: route, | ||
props: props, | ||
error: null, | ||
resolved: resolved | ||
}; | ||
}, function (err) { | ||
// when there is an uncaught error, set it on the response | ||
return { | ||
route: route, | ||
props: props, | ||
error: err, | ||
resolved: null | ||
}; | ||
}); | ||
return ({ error: null, initial: initial, every: every }); | ||
}, function (error) { return ({ error: error, initial: null, every: null }); }); | ||
} | ||
function once(fn) { | ||
var promise = null; | ||
var promise; | ||
var hasRun = false; | ||
@@ -293,4 +275,5 @@ return function () { | ||
var createRoute = function (options) { | ||
var name = options.name, path = options.path, _a = options.pathOptions, pathOptions = _a === void 0 ? {} : _a, _b = options.children, descriptorChildren = _b === void 0 ? [] : _b, _c = options.match, match = _c === void 0 ? {} : _c, extra = options.extra, paramParsers = options.params; | ||
var name = options.name, path = options.path, _a = options.pathOptions, pathOptions = _a === void 0 ? {} : _a, _b = options.children, descriptorChildren = _b === void 0 ? [] : _b, response = options.response, _c = options.on, on = _c === void 0 ? {} : _c, extra = options.extra, paramParsers = options.params; | ||
// end defaults to true, so end has to be hardcoded for it to be false | ||
// set this before setting pathOptions.end for children | ||
var mustBeExact = pathOptions.end == null || pathOptions.end; | ||
@@ -304,2 +287,3 @@ var children = []; | ||
} | ||
// keys is populated by PathToRegexp | ||
var keys = []; | ||
@@ -312,10 +296,9 @@ var re = PathToRegexp(path, keys, pathOptions); | ||
keys: keys.map(function (key) { return key.name; }), | ||
match: { | ||
initial: match.initial && once(match.initial), | ||
every: match.every, | ||
response: match.response | ||
on: { | ||
initial: on.initial && once(on.initial), | ||
every: on.every | ||
}, | ||
extra: extra | ||
}, | ||
match: { | ||
pathMatching: { | ||
re: re, | ||
@@ -325,2 +308,3 @@ keys: keys, | ||
}, | ||
response: response, | ||
children: children, | ||
@@ -331,9 +315,9 @@ paramParsers: paramParsers | ||
function hasMatchFunction(route) { | ||
var match = route.public.match; | ||
return !!(match && (match.every || match.initial)); | ||
function hasAsyncOnFunction(route) { | ||
var on = route.public.on; | ||
return !!(on && (on.every || on.initial)); | ||
} | ||
function hasAsyncRoute(routes) { | ||
return routes.some(function (route) { | ||
if (hasMatchFunction(route)) { | ||
if (hasAsyncOnFunction(route)) { | ||
return true; | ||
@@ -350,3 +334,3 @@ } | ||
if (options === void 0) { options = {}; } | ||
var _a = options, _b = _a.addons, userAddons = _b === void 0 ? [] : _b, _c = _a.sideEffects, sideEffects = _c === void 0 ? [] : _c, cache = _a.cache, pathnameOptions = _a.pathnameOptions, _d = _a.emitRedirects, emitRedirects = _d === void 0 ? true : _d; | ||
var _a = options.route, userInteractions = _a === void 0 ? [] : _a, _b = options.sideEffects, sideEffects = _b === void 0 ? [] : _b, cache = options.cache, pathnameOptions = options.pathnameOptions, _c = options.emitRedirects, emitRedirects = _c === void 0 ? true : _c; | ||
var sync = true; | ||
@@ -364,15 +348,15 @@ var beforeSideEffects = []; | ||
var routes = []; | ||
var registeredAddons = {}; | ||
// add the pathname addon to the provided addons | ||
var allAddons = userAddons.concat(createPathnameAddon(pathnameOptions)); | ||
function setupRoutesAndAddons(routeArray) { | ||
var registeredInteractions = {}; | ||
// add the pathname interaction to the provided interactions | ||
var allInteractions = userInteractions.concat(generatePathname(pathnameOptions)); | ||
function setupRoutesAndInteractions(routeArray) { | ||
routes = routeArray.map(createRoute); | ||
sync = !hasAsyncRoute(routes); | ||
for (var key in registeredAddons) { | ||
delete registeredAddons[key]; | ||
for (var key in registeredInteractions) { | ||
delete registeredInteractions[key]; | ||
} | ||
allAddons.forEach(function (addon) { | ||
addon.reset(); | ||
registeredAddons[addon.name] = addon.get; | ||
registerRoutes(routes, addon); | ||
allInteractions.forEach(function (interaction) { | ||
interaction.reset(); | ||
registeredInteractions[interaction.name] = interaction.get; | ||
registerRoutes(routes, interaction); | ||
}); | ||
@@ -407,2 +391,5 @@ } | ||
function emit(response, navigation) { | ||
// store for current() and respond() | ||
mostRecent.response = response; | ||
mostRecent.navigation = navigation; | ||
var resp = { response: response, navigation: navigation, router: router }; | ||
@@ -421,3 +408,5 @@ beforeSideEffects.forEach(function (fn) { | ||
var fn = oneTimers.pop(); | ||
fn(resp); | ||
if (fn) { | ||
fn(resp); | ||
} | ||
} | ||
@@ -443,12 +432,21 @@ afterSideEffects.forEach(function (fn) { | ||
cacheAndEmit(cachedResponse, navigation); | ||
return; | ||
} | ||
} | ||
var match = matchLocation(pendingNav.location, routes); | ||
// if no routes match, do nothing | ||
if (!match.route) { | ||
console.error("The current location (" + pendingNav.location.pathname + ") has no matching route, " + | ||
'so a response could not be generated. A catch-all route ({ path: "(.*)" }) ' + | ||
"can be used to match locations with no other matching route."); | ||
pendingNav.finish(); | ||
return; | ||
} | ||
if (sync) { | ||
var pendingResponse = createResponse(pendingNav.location, routes); | ||
pendingNav.finish(); | ||
var response = finishResponse(pendingResponse, registeredAddons); | ||
var response = finishResponse(match, registeredInteractions, null); | ||
cacheAndEmit(response, navigation); | ||
} | ||
else { | ||
asyncCreateResponse(pendingNav.location, routes).then(function (pendingResponse) { | ||
resolveRoute(match).then(function (resolved) { | ||
if (pendingNav.cancelled) { | ||
@@ -458,3 +456,3 @@ return; | ||
pendingNav.finish(); | ||
var response = finishResponse(pendingResponse, registeredAddons); | ||
var response = finishResponse(match, registeredInteractions, resolved); | ||
cacheAndEmit(response, navigation); | ||
@@ -470,7 +468,5 @@ }); | ||
if (!response.redirectTo || emitRedirects) { | ||
mostRecent.response = response; | ||
mostRecent.navigation = navigation; | ||
emit(response, navigation); | ||
} | ||
if (response.redirectTo) { | ||
if (response.redirectTo !== undefined) { | ||
history.replace(response.redirectTo); | ||
@@ -480,9 +476,9 @@ } | ||
// now that everything is defined, actually do the setup | ||
setupRoutesAndAddons(routeArray); | ||
setupRoutesAndInteractions(routeArray); | ||
history.respondWith(navigationHandler); | ||
var router = { | ||
addons: registeredAddons, | ||
route: registeredInteractions, | ||
history: history, | ||
respond: respond, | ||
refresh: setupRoutesAndAddons, | ||
replaceRoutes: setupRoutesAndInteractions, | ||
current: function () { | ||
@@ -489,0 +485,0 @@ return { |
1475
dist/curi.js
var Curi = (function () { | ||
'use strict'; | ||
'use strict'; | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
/*! ***************************************************************************** | ||
Copyright (c) Microsoft Corporation. All rights reserved. | ||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use | ||
this file except in compliance with the License. You may obtain a copy of the | ||
License at http://www.apache.org/licenses/LICENSE-2.0 | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | ||
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED | ||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, | ||
MERCHANTABLITY OR NON-INFRINGEMENT. | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
/* global Reflect, Promise */ | ||
See the Apache Version 2.0 License for specific language governing permissions | ||
and limitations under the License. | ||
***************************************************************************** */ | ||
var __assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
function __rest(s, e) { | ||
var t = {}; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) | ||
t[p] = s[p]; | ||
if (s != null && typeof Object.getOwnPropertySymbols === "function") | ||
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0) | ||
t[p[i]] = s[p[i]]; | ||
return t; | ||
} | ||
var __assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
function registerRoutes(routes, interaction, parentData) { | ||
routes.forEach(function (route) { | ||
var data = interaction.register(route.public, parentData); | ||
registerRoutes(route.children, interaction, data); | ||
}); | ||
} | ||
return t; | ||
}; | ||
function registerRoutes(routes, addon, parentData) { | ||
routes.forEach(function (route) { | ||
var data = addon.register(route.public, parentData); | ||
registerRoutes(route.children, addon, data); | ||
}); | ||
} | ||
/** | ||
* Expose `pathToRegexp`. | ||
*/ | ||
var pathToRegexp_1 = pathToRegexp; | ||
var parse_1 = parse; | ||
var compile_1 = compile; | ||
var tokensToFunction_1 = tokensToFunction; | ||
var tokensToRegExp_1 = tokensToRegExp; | ||
/** | ||
* Expose `pathToRegexp`. | ||
*/ | ||
var pathToRegexp_1 = pathToRegexp; | ||
var parse_1 = parse; | ||
var compile_1 = compile; | ||
var tokensToFunction_1 = tokensToFunction; | ||
var tokensToRegExp_1 = tokensToRegExp; | ||
/** | ||
* Default configs. | ||
*/ | ||
var DEFAULT_DELIMITER = '/'; | ||
var DEFAULT_DELIMITERS = './'; | ||
/** | ||
* Default configs. | ||
*/ | ||
var DEFAULT_DELIMITER = '/'; | ||
var DEFAULT_DELIMITERS = './'; | ||
/** | ||
* The main path matching regexp utility. | ||
* | ||
* @type {RegExp} | ||
*/ | ||
var PATH_REGEXP = new RegExp([ | ||
// Match escaped characters that would otherwise appear in future matches. | ||
// This allows the user to escape special characters that won't transform. | ||
'(\\\\.)', | ||
// Match Express-style parameters and un-named parameters with a prefix | ||
// and optional suffixes. Matches appear as: | ||
// | ||
// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"] | ||
// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined] | ||
'(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?' | ||
].join('|'), 'g'); | ||
/** | ||
* The main path matching regexp utility. | ||
* | ||
* @type {RegExp} | ||
*/ | ||
var PATH_REGEXP = new RegExp([ | ||
// Match escaped characters that would otherwise appear in future matches. | ||
// This allows the user to escape special characters that won't transform. | ||
'(\\\\.)', | ||
// Match Express-style parameters and un-named parameters with a prefix | ||
// and optional suffixes. Matches appear as: | ||
// | ||
// "/:test(\\d+)?" => ["/", "test", "\d+", undefined, "?"] | ||
// "/route(\\d+)" => [undefined, undefined, undefined, "\d+", undefined] | ||
'(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?' | ||
].join('|'), 'g'); | ||
/** | ||
* Parse a string for the raw tokens. | ||
* | ||
* @param {string} str | ||
* @param {Object=} options | ||
* @return {!Array} | ||
*/ | ||
function parse (str, options) { | ||
var tokens = []; | ||
var key = 0; | ||
var index = 0; | ||
var path = ''; | ||
var defaultDelimiter = (options && options.delimiter) || DEFAULT_DELIMITER; | ||
var delimiters = (options && options.delimiters) || DEFAULT_DELIMITERS; | ||
var pathEscaped = false; | ||
var res; | ||
/** | ||
* Parse a string for the raw tokens. | ||
* | ||
* @param {string} str | ||
* @param {Object=} options | ||
* @return {!Array} | ||
*/ | ||
function parse (str, options) { | ||
var tokens = []; | ||
var key = 0; | ||
var index = 0; | ||
var path = ''; | ||
var defaultDelimiter = (options && options.delimiter) || DEFAULT_DELIMITER; | ||
var delimiters = (options && options.delimiters) || DEFAULT_DELIMITERS; | ||
var pathEscaped = false; | ||
var res; | ||
while ((res = PATH_REGEXP.exec(str)) !== null) { | ||
var m = res[0]; | ||
var escaped = res[1]; | ||
var offset = res.index; | ||
path += str.slice(index, offset); | ||
index = offset + m.length; | ||
while ((res = PATH_REGEXP.exec(str)) !== null) { | ||
var m = res[0]; | ||
var escaped = res[1]; | ||
var offset = res.index; | ||
path += str.slice(index, offset); | ||
index = offset + m.length; | ||
// Ignore already escaped sequences. | ||
if (escaped) { | ||
path += escaped[1]; | ||
pathEscaped = true; | ||
continue | ||
} | ||
// Ignore already escaped sequences. | ||
if (escaped) { | ||
path += escaped[1]; | ||
pathEscaped = true; | ||
continue | ||
} | ||
var prev = ''; | ||
var next = str[index]; | ||
var name = res[2]; | ||
var capture = res[3]; | ||
var group = res[4]; | ||
var modifier = res[5]; | ||
var prev = ''; | ||
var next = str[index]; | ||
var name = res[2]; | ||
var capture = res[3]; | ||
var group = res[4]; | ||
var modifier = res[5]; | ||
if (!pathEscaped && path.length) { | ||
var k = path.length - 1; | ||
if (!pathEscaped && path.length) { | ||
var k = path.length - 1; | ||
if (delimiters.indexOf(path[k]) > -1) { | ||
prev = path[k]; | ||
path = path.slice(0, k); | ||
} | ||
} | ||
if (delimiters.indexOf(path[k]) > -1) { | ||
prev = path[k]; | ||
path = path.slice(0, k); | ||
// Push the current path onto the tokens. | ||
if (path) { | ||
tokens.push(path); | ||
path = ''; | ||
pathEscaped = false; | ||
} | ||
var partial = prev !== '' && next !== undefined && next !== prev; | ||
var repeat = modifier === '+' || modifier === '*'; | ||
var optional = modifier === '?' || modifier === '*'; | ||
var delimiter = prev || defaultDelimiter; | ||
var pattern = capture || group; | ||
tokens.push({ | ||
name: name || key++, | ||
prefix: prev, | ||
delimiter: delimiter, | ||
optional: optional, | ||
repeat: repeat, | ||
partial: partial, | ||
pattern: pattern ? escapeGroup(pattern) : '[^' + escapeString(delimiter) + ']+?' | ||
}); | ||
} | ||
// Push any remaining characters. | ||
if (path || index < str.length) { | ||
tokens.push(path + str.substr(index)); | ||
} | ||
return tokens | ||
} | ||
// Push the current path onto the tokens. | ||
if (path) { | ||
tokens.push(path); | ||
path = ''; | ||
pathEscaped = false; | ||
/** | ||
* Compile a string to a template function for the path. | ||
* | ||
* @param {string} str | ||
* @param {Object=} options | ||
* @return {!function(Object=, Object=)} | ||
*/ | ||
function compile (str, options) { | ||
return tokensToFunction(parse(str, options)) | ||
} | ||
var partial = prev !== '' && next !== undefined && next !== prev; | ||
var repeat = modifier === '+' || modifier === '*'; | ||
var optional = modifier === '?' || modifier === '*'; | ||
var delimiter = prev || defaultDelimiter; | ||
var pattern = capture || group; | ||
/** | ||
* Expose a method for transforming tokens into the path function. | ||
*/ | ||
function tokensToFunction (tokens) { | ||
// Compile all the tokens into regexps. | ||
var matches = new Array(tokens.length); | ||
tokens.push({ | ||
name: name || key++, | ||
prefix: prev, | ||
delimiter: delimiter, | ||
optional: optional, | ||
repeat: repeat, | ||
partial: partial, | ||
pattern: pattern ? escapeGroup(pattern) : '[^' + escapeString(delimiter) + ']+?' | ||
}); | ||
} | ||
// Compile all the patterns before compilation. | ||
for (var i = 0; i < tokens.length; i++) { | ||
if (typeof tokens[i] === 'object') { | ||
matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$'); | ||
} | ||
} | ||
// Push any remaining characters. | ||
if (path || index < str.length) { | ||
tokens.push(path + str.substr(index)); | ||
} | ||
return function (data, options) { | ||
var path = ''; | ||
var encode = (options && options.encode) || encodeURIComponent; | ||
return tokens | ||
} | ||
for (var i = 0; i < tokens.length; i++) { | ||
var token = tokens[i]; | ||
/** | ||
* Compile a string to a template function for the path. | ||
* | ||
* @param {string} str | ||
* @param {Object=} options | ||
* @return {!function(Object=, Object=)} | ||
*/ | ||
function compile (str, options) { | ||
return tokensToFunction(parse(str, options)) | ||
} | ||
if (typeof token === 'string') { | ||
path += token; | ||
continue | ||
} | ||
/** | ||
* Expose a method for transforming tokens into the path function. | ||
*/ | ||
function tokensToFunction (tokens) { | ||
// Compile all the tokens into regexps. | ||
var matches = new Array(tokens.length); | ||
var value = data ? data[token.name] : undefined; | ||
var segment; | ||
// Compile all the patterns before compilation. | ||
for (var i = 0; i < tokens.length; i++) { | ||
if (typeof tokens[i] === 'object') { | ||
matches[i] = new RegExp('^(?:' + tokens[i].pattern + ')$'); | ||
} | ||
} | ||
if (Array.isArray(value)) { | ||
if (!token.repeat) { | ||
throw new TypeError('Expected "' + token.name + '" to not repeat, but got array') | ||
} | ||
return function (data, options) { | ||
var path = ''; | ||
var encode = (options && options.encode) || encodeURIComponent; | ||
if (value.length === 0) { | ||
if (token.optional) continue | ||
for (var i = 0; i < tokens.length; i++) { | ||
var token = tokens[i]; | ||
throw new TypeError('Expected "' + token.name + '" to not be empty') | ||
} | ||
if (typeof token === 'string') { | ||
path += token; | ||
continue | ||
} | ||
for (var j = 0; j < value.length; j++) { | ||
segment = encode(value[j], token); | ||
var value = data ? data[token.name] : undefined; | ||
var segment; | ||
if (!matches[i].test(segment)) { | ||
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '"') | ||
} | ||
if (Array.isArray(value)) { | ||
if (!token.repeat) { | ||
throw new TypeError('Expected "' + token.name + '" to not repeat, but got array') | ||
} | ||
path += (j === 0 ? token.prefix : token.delimiter) + segment; | ||
} | ||
if (value.length === 0) { | ||
if (token.optional) continue | ||
continue | ||
} | ||
throw new TypeError('Expected "' + token.name + '" to not be empty') | ||
} | ||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { | ||
segment = encode(String(value), token); | ||
for (var j = 0; j < value.length; j++) { | ||
segment = encode(value[j], token); | ||
if (!matches[i].test(segment)) { | ||
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but got "' + segment + '"') | ||
} | ||
if (!matches[i].test(segment)) { | ||
throw new TypeError('Expected all "' + token.name + '" to match "' + token.pattern + '"') | ||
path += token.prefix + segment; | ||
continue | ||
} | ||
path += (j === 0 ? token.prefix : token.delimiter) + segment; | ||
} | ||
if (token.optional) { | ||
// Prepend partial segment prefixes. | ||
if (token.partial) path += token.prefix; | ||
continue | ||
} | ||
continue | ||
} | ||
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') { | ||
segment = encode(String(value), token); | ||
if (!matches[i].test(segment)) { | ||
throw new TypeError('Expected "' + token.name + '" to match "' + token.pattern + '", but got "' + segment + '"') | ||
throw new TypeError('Expected "' + token.name + '" to be ' + (token.repeat ? 'an array' : 'a string')) | ||
} | ||
path += token.prefix + segment; | ||
continue | ||
return path | ||
} | ||
} | ||
if (token.optional) { | ||
// Prepend partial segment prefixes. | ||
if (token.partial) path += token.prefix; | ||
/** | ||
* Escape a regular expression string. | ||
* | ||
* @param {string} str | ||
* @return {string} | ||
*/ | ||
function escapeString (str) { | ||
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1') | ||
} | ||
continue | ||
} | ||
/** | ||
* Escape the capturing group by escaping special characters and meaning. | ||
* | ||
* @param {string} group | ||
* @return {string} | ||
*/ | ||
function escapeGroup (group) { | ||
return group.replace(/([=!:$/()])/g, '\\$1') | ||
} | ||
throw new TypeError('Expected "' + token.name + '" to be ' + (token.repeat ? 'an array' : 'a string')) | ||
/** | ||
* Get the flags for a regexp from the options. | ||
* | ||
* @param {Object} options | ||
* @return {string} | ||
*/ | ||
function flags (options) { | ||
return options && options.sensitive ? '' : 'i' | ||
} | ||
return path | ||
} | ||
} | ||
/** | ||
* Pull out keys from a regexp. | ||
* | ||
* @param {!RegExp} path | ||
* @param {Array=} keys | ||
* @return {!RegExp} | ||
*/ | ||
function regexpToRegexp (path, keys) { | ||
if (!keys) return path | ||
/** | ||
* Escape a regular expression string. | ||
* | ||
* @param {string} str | ||
* @return {string} | ||
*/ | ||
function escapeString (str) { | ||
return str.replace(/([.+*?=^!:${}()[\]|/\\])/g, '\\$1') | ||
} | ||
// Use a negative lookahead to match only capturing groups. | ||
var groups = path.source.match(/\((?!\?)/g); | ||
/** | ||
* Escape the capturing group by escaping special characters and meaning. | ||
* | ||
* @param {string} group | ||
* @return {string} | ||
*/ | ||
function escapeGroup (group) { | ||
return group.replace(/([=!:$/()])/g, '\\$1') | ||
} | ||
if (groups) { | ||
for (var i = 0; i < groups.length; i++) { | ||
keys.push({ | ||
name: i, | ||
prefix: null, | ||
delimiter: null, | ||
optional: false, | ||
repeat: false, | ||
partial: false, | ||
pattern: null | ||
}); | ||
} | ||
} | ||
/** | ||
* Get the flags for a regexp from the options. | ||
* | ||
* @param {Object} options | ||
* @return {string} | ||
*/ | ||
function flags (options) { | ||
return options && options.sensitive ? '' : 'i' | ||
} | ||
return path | ||
} | ||
/** | ||
* Pull out keys from a regexp. | ||
* | ||
* @param {!RegExp} path | ||
* @param {Array=} keys | ||
* @return {!RegExp} | ||
*/ | ||
function regexpToRegexp (path, keys) { | ||
if (!keys) return path | ||
/** | ||
* Transform an array into a regexp. | ||
* | ||
* @param {!Array} path | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function arrayToRegexp (path, keys, options) { | ||
var parts = []; | ||
// Use a negative lookahead to match only capturing groups. | ||
var groups = path.source.match(/\((?!\?)/g); | ||
for (var i = 0; i < path.length; i++) { | ||
parts.push(pathToRegexp(path[i], keys, options).source); | ||
} | ||
if (groups) { | ||
for (var i = 0; i < groups.length; i++) { | ||
keys.push({ | ||
name: i, | ||
prefix: null, | ||
delimiter: null, | ||
optional: false, | ||
repeat: false, | ||
partial: false, | ||
pattern: null | ||
}); | ||
return new RegExp('(?:' + parts.join('|') + ')', flags(options)) | ||
} | ||
} | ||
return path | ||
} | ||
/** | ||
* Create a path regexp from string input. | ||
* | ||
* @param {string} path | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function stringToRegexp (path, keys, options) { | ||
return tokensToRegExp(parse(path, options), keys, options) | ||
} | ||
/** | ||
* Transform an array into a regexp. | ||
* | ||
* @param {!Array} path | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function arrayToRegexp (path, keys, options) { | ||
var parts = []; | ||
/** | ||
* Expose a function for taking tokens and returning a RegExp. | ||
* | ||
* @param {!Array} tokens | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function tokensToRegExp (tokens, keys, options) { | ||
options = options || {}; | ||
for (var i = 0; i < path.length; i++) { | ||
parts.push(pathToRegexp(path[i], keys, options).source); | ||
} | ||
var strict = options.strict; | ||
var end = options.end !== false; | ||
var delimiter = escapeString(options.delimiter || DEFAULT_DELIMITER); | ||
var delimiters = options.delimiters || DEFAULT_DELIMITERS; | ||
var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|'); | ||
var route = ''; | ||
var isEndDelimited = false; | ||
return new RegExp('(?:' + parts.join('|') + ')', flags(options)) | ||
} | ||
// Iterate over the tokens and create our regexp string. | ||
for (var i = 0; i < tokens.length; i++) { | ||
var token = tokens[i]; | ||
/** | ||
* Create a path regexp from string input. | ||
* | ||
* @param {string} path | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function stringToRegexp (path, keys, options) { | ||
return tokensToRegExp(parse(path, options), keys, options) | ||
} | ||
if (typeof token === 'string') { | ||
route += escapeString(token); | ||
isEndDelimited = i === tokens.length - 1 && delimiters.indexOf(token[token.length - 1]) > -1; | ||
} else { | ||
var prefix = escapeString(token.prefix); | ||
var capture = token.repeat | ||
? '(?:' + token.pattern + ')(?:' + prefix + '(?:' + token.pattern + '))*' | ||
: token.pattern; | ||
/** | ||
* Expose a function for taking tokens and returning a RegExp. | ||
* | ||
* @param {!Array} tokens | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function tokensToRegExp (tokens, keys, options) { | ||
options = options || {}; | ||
if (keys) keys.push(token); | ||
var strict = options.strict; | ||
var end = options.end !== false; | ||
var delimiter = escapeString(options.delimiter || DEFAULT_DELIMITER); | ||
var delimiters = options.delimiters || DEFAULT_DELIMITERS; | ||
var endsWith = [].concat(options.endsWith || []).map(escapeString).concat('$').join('|'); | ||
var route = ''; | ||
var isEndDelimited = false; | ||
if (token.optional) { | ||
if (token.partial) { | ||
route += prefix + '(' + capture + ')?'; | ||
} else { | ||
route += '(?:' + prefix + '(' + capture + '))?'; | ||
} | ||
} else { | ||
route += prefix + '(' + capture + ')'; | ||
} | ||
} | ||
} | ||
// Iterate over the tokens and create our regexp string. | ||
for (var i = 0; i < tokens.length; i++) { | ||
var token = tokens[i]; | ||
if (end) { | ||
if (!strict) route += '(?:' + delimiter + ')?'; | ||
if (typeof token === 'string') { | ||
route += escapeString(token); | ||
isEndDelimited = i === tokens.length - 1 && delimiters.indexOf(token[token.length - 1]) > -1; | ||
} else { | ||
var prefix = escapeString(token.prefix); | ||
var capture = token.repeat | ||
? '(?:' + token.pattern + ')(?:' + prefix + '(?:' + token.pattern + '))*' | ||
: token.pattern; | ||
if (keys) keys.push(token); | ||
if (token.optional) { | ||
if (token.partial) { | ||
route += prefix + '(' + capture + ')?'; | ||
} else { | ||
route += '(?:' + prefix + '(' + capture + '))?'; | ||
} | ||
route += endsWith === '$' ? '$' : '(?=' + endsWith + ')'; | ||
} else { | ||
route += prefix + '(' + capture + ')'; | ||
if (!strict) route += '(?:' + delimiter + '(?=' + endsWith + '))?'; | ||
if (!isEndDelimited) route += '(?=' + delimiter + '|' + endsWith + ')'; | ||
} | ||
return new RegExp('^' + route, flags(options)) | ||
} | ||
} | ||
if (end) { | ||
if (!strict) route += '(?:' + delimiter + ')?'; | ||
/** | ||
* Normalize the given path string, returning a regular expression. | ||
* | ||
* An empty array can be passed in for the keys, which will hold the | ||
* placeholder key descriptions. For example, using `/user/:id`, `keys` will | ||
* contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. | ||
* | ||
* @param {(string|RegExp|Array)} path | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function pathToRegexp (path, keys, options) { | ||
if (path instanceof RegExp) { | ||
return regexpToRegexp(path, keys) | ||
} | ||
route += endsWith === '$' ? '$' : '(?=' + endsWith + ')'; | ||
} else { | ||
if (!strict) route += '(?:' + delimiter + '(?=' + endsWith + '))?'; | ||
if (!isEndDelimited) route += '(?=' + delimiter + '|' + endsWith + ')'; | ||
} | ||
if (Array.isArray(path)) { | ||
return arrayToRegexp(/** @type {!Array} */ (path), keys, options) | ||
} | ||
return new RegExp('^' + route, flags(options)) | ||
} | ||
return stringToRegexp(/** @type {string} */ (path), keys, options) | ||
} | ||
pathToRegexp_1.parse = parse_1; | ||
pathToRegexp_1.compile = compile_1; | ||
pathToRegexp_1.tokensToFunction = tokensToFunction_1; | ||
pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; | ||
/** | ||
* Normalize the given path string, returning a regular expression. | ||
* | ||
* An empty array can be passed in for the keys, which will hold the | ||
* placeholder key descriptions. For example, using `/user/:id`, `keys` will | ||
* contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`. | ||
* | ||
* @param {(string|RegExp|Array)} path | ||
* @param {Array=} keys | ||
* @param {Object=} options | ||
* @return {!RegExp} | ||
*/ | ||
function pathToRegexp (path, keys, options) { | ||
if (path instanceof RegExp) { | ||
return regexpToRegexp(path, keys) | ||
} | ||
var withLeadingSlash = function (path) { | ||
return path.charAt(0) === '/' ? path : '/' + path; | ||
}; | ||
var stripLeadingSlash = function (path) { | ||
return path.charAt(0) === '/' ? path.slice(1) : path; | ||
}; | ||
var withTrailingSlash = function (path) { | ||
return path.charAt(path.length - 1) === '/' ? path : path + '/'; | ||
}; | ||
var join = function (beginning, end) { | ||
return withTrailingSlash(beginning) + end; | ||
}; | ||
if (Array.isArray(path)) { | ||
return arrayToRegexp(/** @type {!Array} */ (path), keys, options) | ||
} | ||
return stringToRegexp(/** @type {string} */ (path), keys, options) | ||
} | ||
pathToRegexp_1.parse = parse_1; | ||
pathToRegexp_1.compile = compile_1; | ||
pathToRegexp_1.tokensToFunction = tokensToFunction_1; | ||
pathToRegexp_1.tokensToRegExp = tokensToRegExp_1; | ||
var withLeadingSlash = function (path) { | ||
return path.charAt(0) === '/' ? path : '/' + path; | ||
}; | ||
var stripLeadingSlash = function (path) { | ||
return path.charAt(0) === '/' ? path.slice(1) : path; | ||
}; | ||
var withTrailingSlash = function (path) { | ||
return path.charAt(path.length - 1) === '/' ? path : path + '/'; | ||
}; | ||
var join = function (beginning, end) { | ||
return withTrailingSlash(beginning) + end; | ||
}; | ||
function createPathnameAddon(options) { | ||
var knownPaths = {}; | ||
var cache = {}; | ||
return { | ||
name: "pathname", | ||
register: function (route, parent) { | ||
var name = route.name, path = route.path; | ||
if (knownPaths[name] !== undefined) { | ||
console.warn('A route with the name "' + | ||
name + | ||
'" already exists. Each route should' + | ||
"have a unique name. By registering a route with a name that already exists, " + | ||
"you are overwriting the existing one. This may break your application."); | ||
function generatePathname(options) { | ||
var knownPaths = {}; | ||
var cache = {}; | ||
return { | ||
name: "pathname", | ||
register: function (route, parent) { | ||
var name = route.name, path = route.path; | ||
if (knownPaths[name] !== undefined) { | ||
console.warn('A route with the name "' + | ||
name + | ||
'" already exists. Each route should' + | ||
"have a unique name. By registering a route with a name that already exists, " + | ||
"you are overwriting the existing one. This may break your application."); | ||
} | ||
var base; | ||
if (parent && knownPaths[parent]) { | ||
base = knownPaths[parent]; | ||
} | ||
knownPaths[name] = base ? join(base, path) : path; | ||
return name; | ||
}, | ||
get: function (name, params) { | ||
if (knownPaths[name] == null) { | ||
console.error("Could not generate pathname for " + name + " because it is not registered."); | ||
return; | ||
} | ||
var compile = cache[name] | ||
? cache[name] | ||
: (cache[name] = pathToRegexp_1.compile(knownPaths[name])); | ||
return withLeadingSlash(compile(params, options)); | ||
}, | ||
reset: function () { | ||
knownPaths = {}; | ||
cache = {}; | ||
} | ||
var base; | ||
if (parent && knownPaths[parent]) { | ||
base = knownPaths[parent]; | ||
} | ||
knownPaths[name] = base ? join(base, path) : path; | ||
return name; | ||
}, | ||
get: function (name, params) { | ||
if (knownPaths[name] == null) { | ||
console.error("Could not generate pathname for " + name + " because it is not registered."); | ||
return; | ||
} | ||
var compile = cache[name] | ||
? cache[name] | ||
: (cache[name] = pathToRegexp_1.compile(knownPaths[name])); | ||
return withLeadingSlash(compile(params, options)); | ||
}, | ||
reset: function () { | ||
knownPaths = {}; | ||
cache = {}; | ||
} | ||
}; | ||
} | ||
}; | ||
} | ||
function routeProperties(route, props) { | ||
return { | ||
params: props.params, | ||
location: props.location, | ||
name: route.public.name | ||
}; | ||
} | ||
function responseSetters(props, addons) { | ||
return { | ||
redirect: function (redProps) { | ||
var name = redProps.name, params = redProps.params, query = redProps.query, hash = redProps.hash, state = redProps.state, _a = redProps.status, status = _a === void 0 ? 301 : _a; | ||
props.status = status; | ||
var pathname = addons.pathname(name, params); | ||
props.redirectTo = { | ||
pathname: pathname, | ||
query: query, | ||
hash: hash, | ||
state: state | ||
}; | ||
}, | ||
error: function (err) { | ||
props.error = err; | ||
}, | ||
status: function (code) { | ||
props.status = code; | ||
}, | ||
data: function (data) { | ||
props.data = data; | ||
}, | ||
body: function (body) { | ||
props.body = body; | ||
}, | ||
title: function (title) { | ||
props.title = title; | ||
function createRedirect(redirectTo, interactions) { | ||
var name = redirectTo.name, params = redirectTo.params, rest = __rest(redirectTo, ["name", "params"]); | ||
var pathname = interactions.pathname(name, params); | ||
return __assign({ pathname: pathname }, rest); | ||
} | ||
function finishResponse(match, interactions, resolved) { | ||
var route = match.route, response = match.response; | ||
if (!route.response) { | ||
return response; | ||
} | ||
}; | ||
} | ||
function freezeResponse(route, props) { | ||
var response = Object.assign({ | ||
key: props.location.key, | ||
name: route ? route.public.name : undefined | ||
}, props); | ||
return response; | ||
} | ||
function finishResponse(pending, addons) { | ||
var error = pending.error, resolved = pending.resolved, route = pending.route, props = pending.props; | ||
if (route && route.public.match.response) { | ||
route.public.match.response({ | ||
error: error, | ||
var responseModifiers = route.response({ | ||
resolved: resolved, | ||
route: routeProperties(route, props), | ||
set: responseSetters(props, addons), | ||
addons: addons | ||
name: response.name, | ||
params: __assign({}, response.params), | ||
location: response.location, | ||
route: interactions | ||
}); | ||
if (!responseModifiers) { | ||
return response; | ||
} | ||
var settableProperties = [ | ||
"status", | ||
"error", | ||
"body", | ||
"data", | ||
"title", | ||
"redirectTo" | ||
]; | ||
// only merge the valid properties onto the response | ||
settableProperties.forEach(function (p) { | ||
if (responseModifiers.hasOwnProperty(p)) { | ||
if (p === "redirectTo") { | ||
// special case | ||
response[p] = createRedirect(responseModifiers[p], interactions); | ||
} | ||
else { | ||
response[p] = responseModifiers[p]; | ||
} | ||
} | ||
}); | ||
return response; | ||
} | ||
return freezeResponse(route, props); | ||
} | ||
function matchRoute(route, pathname, matches, parentPath) { | ||
var testPath = stripLeadingSlash(pathname); | ||
var _a = route.match, re = _a.re, keys = _a.keys, mustBeExact = _a.mustBeExact; | ||
var children = route.children; | ||
var match = re.exec(testPath); | ||
if (!match) { | ||
return false; | ||
} | ||
var segment = match[0], parsed = match.slice(1); | ||
var params = {}; | ||
keys.forEach(function (key, index) { | ||
params[key.name] = parsed[index]; | ||
}); | ||
var uriString = parentPath != null ? join(parentPath, segment) : withLeadingSlash(segment); | ||
matches.push({ route: route, params: params }); | ||
// if there are no children, then we accept the match | ||
if (!children || !children.length) { | ||
return true; | ||
} | ||
// children only need to match against unmatched segments | ||
var remainder = testPath.slice(segment.length); | ||
var notExact = !!remainder.length; | ||
var hasChildMatch = children.some(function (c) { | ||
return matchRoute(c, remainder, matches, uriString); | ||
}); | ||
// if the route has children, but none of them match, remove the match unless it | ||
// is exact | ||
if (mustBeExact && notExact && !hasChildMatch) { | ||
matches.pop(); | ||
return false; | ||
} | ||
return true; | ||
} | ||
function parseParams(params, fns) { | ||
if (!fns) { | ||
return params; | ||
} | ||
var output = {}; | ||
// For each param, attempt to parse it. However, if that | ||
// fails, fall back to the string value. | ||
for (var key in params) { | ||
var value = params[key]; | ||
var fn = fns[key]; | ||
if (fn) { | ||
try { | ||
value = fn(value); | ||
function parseParams(params, fns) { | ||
if (!fns) { | ||
return params; | ||
} | ||
var output = {}; | ||
// For each param, attempt to parse it. However, if that | ||
// fails, fall back to the string value. | ||
for (var key in params) { | ||
var value = params[key]; | ||
var fn = fns[key]; | ||
if (fn) { | ||
try { | ||
value = fn(value); | ||
} | ||
catch (e) { | ||
console.error(e); | ||
value = params[key]; | ||
} | ||
} | ||
catch (e) { | ||
console.error(e); | ||
value = params[key]; | ||
} | ||
output[key] = value; | ||
} | ||
output[key] = value; | ||
return output; | ||
} | ||
return output; | ||
} | ||
function matchLocation(location, routes) { | ||
var matches = []; | ||
var partials = []; | ||
var params = {}; | ||
var route; | ||
// determine which route(s) match, then use the exact match | ||
// as the matched route and the rest as partial routes | ||
routes.some(function (route) { return matchRoute(route, location.pathname, matches); }); | ||
if (matches.length) { | ||
var bestMatch = matches.pop(); | ||
matches.forEach(function (m) { | ||
partials.push(m.route.public.name); | ||
Object.assign(params, parseParams(m.params, m.route.paramParsers)); | ||
function matchRoute(route, pathname) { | ||
var testPath = stripLeadingSlash(pathname); | ||
var regExpMatch = route.pathMatching.re.exec(testPath); | ||
if (!regExpMatch) { | ||
return []; | ||
} | ||
var matchedSegment = regExpMatch[0], parsed = regExpMatch.slice(1); | ||
var params = {}; | ||
route.pathMatching.keys.forEach(function (key, index) { | ||
params[key.name] = parsed[index]; | ||
}); | ||
route = bestMatch.route; | ||
Object.assign(params, parseParams(bestMatch.params, route.paramParsers)); | ||
var matches = [{ route: route, params: params }]; | ||
// if there are no children routes, immediately accept the match | ||
if (!route.children.length) { | ||
return matches; | ||
} | ||
// children only need to match against unmatched segments | ||
var remainingSegments = testPath.slice(matchedSegment.length); | ||
var childrenLength = route.children.length; | ||
for (var i = 0; i < childrenLength; i++) { | ||
var matched = matchRoute(route.children[i], remainingSegments); | ||
if (matched.length) { | ||
matches = matches.concat(matched); | ||
break; | ||
} | ||
} | ||
// return matches if a child route matches or this route matches exactly | ||
return matches.length > 1 || | ||
(route.pathMatching.mustBeExact && remainingSegments.length === 0) | ||
? matches | ||
: []; | ||
} | ||
// start building the properties of the response object | ||
var props = { | ||
location: location, | ||
params: params, | ||
partials: partials, | ||
status: route != null ? 200 : 404, | ||
body: undefined, | ||
data: undefined, | ||
title: "" | ||
}; | ||
return { route: route, props: props }; | ||
} | ||
function createResponse(location, routes) { | ||
return __assign({}, matchLocation(location, routes), { resolved: null, error: null }); | ||
} | ||
function asyncCreateResponse(location, routes) { | ||
var _a = matchLocation(location, routes), route = _a.route, props = _a.props; | ||
return loadRoute(route, props); | ||
} | ||
/* | ||
* This will call any initial/every match functions for the matching route | ||
*/ | ||
function loadRoute(route, props) { | ||
if (!route) { | ||
return Promise.resolve({ | ||
route: route, | ||
props: props | ||
function createMatch(routeMatches, location) { | ||
var partials = []; | ||
var params = {}; | ||
var bestMatch = routeMatches.pop(); | ||
// handle ancestor routes | ||
routeMatches.forEach(function (match) { | ||
partials.push(match.route.public.name); | ||
Object.assign(params, parseParams(match.params, match.route.paramParsers)); | ||
}); | ||
} | ||
var match = route.public.match; | ||
return Promise.all([ | ||
match.initial ? match.initial() : undefined, | ||
match.every ? match.every(routeProperties(route, props)) : undefined | ||
]).then(function (_a) { | ||
var initial = _a[0], every = _a[1]; | ||
var resolved = !match.initial && !match.every ? null : { initial: initial, every: every }; | ||
// handle best match | ||
Object.assign(params, parseParams(bestMatch.params, bestMatch.route.paramParsers)); | ||
return { | ||
route: route, | ||
props: props, | ||
error: null, | ||
resolved: resolved | ||
route: bestMatch.route, | ||
response: { | ||
location: location, | ||
key: location.key, | ||
name: bestMatch.route.public.name, | ||
params: params, | ||
partials: partials, | ||
status: 200, | ||
body: undefined, | ||
data: undefined, | ||
title: "" | ||
} | ||
}; | ||
}, function (err) { | ||
// when there is an uncaught error, set it on the response | ||
} | ||
function matchLocation(location, routes) { | ||
// determine which route(s) match, then use the exact match | ||
// as the matched route and the rest as partial routes | ||
var routeLength = routes.length; | ||
for (var i = 0; i < routeLength; i++) { | ||
var routeMatches = matchRoute(routes[i], location.pathname); | ||
if (routeMatches.length) { | ||
return createMatch(routeMatches, location); | ||
} | ||
} | ||
// no matching route | ||
return { | ||
route: route, | ||
props: props, | ||
error: err, | ||
resolved: null | ||
route: undefined, | ||
response: undefined | ||
}; | ||
}); | ||
} | ||
} | ||
function once(fn) { | ||
var promise = null; | ||
var hasRun = false; | ||
return function () { | ||
if (hasRun) { | ||
/* | ||
* This will call any initial/every match functions for the matching route | ||
*/ | ||
function resolveRoute(match) { | ||
var response = match.response; | ||
var on = match.route.public.on; | ||
return Promise.all([ | ||
on.initial && on.initial(), | ||
on.every && | ||
on.every({ | ||
name: response.name, | ||
params: __assign({}, response.params), | ||
location: response.location | ||
}) | ||
]).then(function (_a) { | ||
var initial = _a[0], every = _a[1]; | ||
return ({ error: null, initial: initial, every: every }); | ||
}, function (error) { return ({ error: error, initial: null, every: null }); }); | ||
} | ||
function once(fn) { | ||
var promise; | ||
var hasRun = false; | ||
return function () { | ||
if (hasRun) { | ||
return promise; | ||
} | ||
promise = fn(); | ||
hasRun = true; | ||
return promise; | ||
}; | ||
} | ||
var createRoute = function (options) { | ||
var name = options.name, path = options.path, _a = options.pathOptions, pathOptions = _a === void 0 ? {} : _a, _b = options.children, descriptorChildren = _b === void 0 ? [] : _b, response = options.response, _c = options.on, on = _c === void 0 ? {} : _c, extra = options.extra, paramParsers = options.params; | ||
// end defaults to true, so end has to be hardcoded for it to be false | ||
// set this before setting pathOptions.end for children | ||
var mustBeExact = pathOptions.end == null || pathOptions.end; | ||
var children = []; | ||
// when we have child routes, we need to perform non-end matching and | ||
// create route objects for each child | ||
if (descriptorChildren.length) { | ||
pathOptions.end = false; | ||
children = descriptorChildren.map(createRoute); | ||
} | ||
promise = fn(); | ||
hasRun = true; | ||
return promise; | ||
// keys is populated by PathToRegexp | ||
var keys = []; | ||
var re = pathToRegexp_1(path, keys, pathOptions); | ||
return { | ||
public: { | ||
name: name, | ||
path: path, | ||
keys: keys.map(function (key) { return key.name; }), | ||
on: { | ||
initial: on.initial && once(on.initial), | ||
every: on.every | ||
}, | ||
extra: extra | ||
}, | ||
pathMatching: { | ||
re: re, | ||
keys: keys, | ||
mustBeExact: mustBeExact | ||
}, | ||
response: response, | ||
children: children, | ||
paramParsers: paramParsers | ||
}; | ||
}; | ||
} | ||
var createRoute = function (options) { | ||
var name = options.name, path = options.path, _a = options.pathOptions, pathOptions = _a === void 0 ? {} : _a, _b = options.children, descriptorChildren = _b === void 0 ? [] : _b, _c = options.match, match = _c === void 0 ? {} : _c, extra = options.extra, paramParsers = options.params; | ||
// end defaults to true, so end has to be hardcoded for it to be false | ||
var mustBeExact = pathOptions.end == null || pathOptions.end; | ||
var children = []; | ||
// when we have child routes, we need to perform non-end matching and | ||
// create route objects for each child | ||
if (descriptorChildren.length) { | ||
pathOptions.end = false; | ||
children = descriptorChildren.map(createRoute); | ||
function hasAsyncOnFunction(route) { | ||
var on = route.public.on; | ||
return !!(on && (on.every || on.initial)); | ||
} | ||
var keys = []; | ||
var re = pathToRegexp_1(path, keys, pathOptions); | ||
return { | ||
public: { | ||
name: name, | ||
path: path, | ||
keys: keys.map(function (key) { return key.name; }), | ||
match: { | ||
initial: match.initial && once(match.initial), | ||
every: match.every, | ||
response: match.response | ||
}, | ||
extra: extra | ||
}, | ||
match: { | ||
re: re, | ||
keys: keys, | ||
mustBeExact: mustBeExact | ||
}, | ||
children: children, | ||
paramParsers: paramParsers | ||
}; | ||
}; | ||
function hasMatchFunction(route) { | ||
var match = route.public.match; | ||
return !!(match && (match.every || match.initial)); | ||
} | ||
function hasAsyncRoute(routes) { | ||
return routes.some(function (route) { | ||
if (hasMatchFunction(route)) { | ||
return true; | ||
} | ||
if (route.children.length) { | ||
return hasAsyncRoute(route.children); | ||
} | ||
return false; | ||
}); | ||
} | ||
function createRouter(history, routeArray, options) { | ||
if (options === void 0) { options = {}; } | ||
var _a = options, _b = _a.addons, userAddons = _b === void 0 ? [] : _b, _c = _a.sideEffects, sideEffects = _c === void 0 ? [] : _c, cache = _a.cache, pathnameOptions = _a.pathnameOptions, _d = _a.emitRedirects, emitRedirects = _d === void 0 ? true : _d; | ||
var sync = true; | ||
var beforeSideEffects = []; | ||
var afterSideEffects = []; | ||
sideEffects.forEach(function (se) { | ||
if (se.after) { | ||
afterSideEffects.push(se.fn); | ||
} | ||
else { | ||
beforeSideEffects.push(se.fn); | ||
} | ||
}); | ||
var routes = []; | ||
var registeredAddons = {}; | ||
// add the pathname addon to the provided addons | ||
var allAddons = userAddons.concat(createPathnameAddon(pathnameOptions)); | ||
function setupRoutesAndAddons(routeArray) { | ||
routes = routeArray.map(createRoute); | ||
sync = !hasAsyncRoute(routes); | ||
for (var key in registeredAddons) { | ||
delete registeredAddons[key]; | ||
} | ||
allAddons.forEach(function (addon) { | ||
addon.reset(); | ||
registeredAddons[addon.name] = addon.get; | ||
registerRoutes(routes, addon); | ||
function hasAsyncRoute(routes) { | ||
return routes.some(function (route) { | ||
if (hasAsyncOnFunction(route)) { | ||
return true; | ||
} | ||
if (route.children.length) { | ||
return hasAsyncRoute(route.children); | ||
} | ||
return false; | ||
}); | ||
} | ||
var responseHandlers = []; | ||
var oneTimers = []; | ||
var mostRecent = { | ||
response: null, | ||
navigation: null | ||
}; | ||
function respond(fn, options) { | ||
var _a = options || {}, _b = _a.observe, observe = _b === void 0 ? false : _b, _c = _a.initial, initial = _c === void 0 ? true : _c; | ||
if (observe) { | ||
var newLength_1 = responseHandlers.push(fn); | ||
if (mostRecent.response && initial) { | ||
fn.call(null, __assign({}, mostRecent, { router: router })); | ||
function createRouter(history, routeArray, options) { | ||
if (options === void 0) { options = {}; } | ||
var _a = options.route, userInteractions = _a === void 0 ? [] : _a, _b = options.sideEffects, sideEffects = _b === void 0 ? [] : _b, cache = options.cache, pathnameOptions = options.pathnameOptions, _c = options.emitRedirects, emitRedirects = _c === void 0 ? true : _c; | ||
var sync = true; | ||
var beforeSideEffects = []; | ||
var afterSideEffects = []; | ||
sideEffects.forEach(function (se) { | ||
if (se.after) { | ||
afterSideEffects.push(se.fn); | ||
} | ||
return function () { | ||
responseHandlers[newLength_1 - 1] = null; | ||
}; | ||
else { | ||
beforeSideEffects.push(se.fn); | ||
} | ||
}); | ||
var routes = []; | ||
var registeredInteractions = {}; | ||
// add the pathname interaction to the provided interactions | ||
var allInteractions = userInteractions.concat(generatePathname(pathnameOptions)); | ||
function setupRoutesAndInteractions(routeArray) { | ||
routes = routeArray.map(createRoute); | ||
sync = !hasAsyncRoute(routes); | ||
for (var key in registeredInteractions) { | ||
delete registeredInteractions[key]; | ||
} | ||
allInteractions.forEach(function (interaction) { | ||
interaction.reset(); | ||
registeredInteractions[interaction.name] = interaction.get; | ||
registerRoutes(routes, interaction); | ||
}); | ||
} | ||
else { | ||
if (mostRecent.response && initial) { | ||
fn.call(null, __assign({}, mostRecent, { router: router })); | ||
var responseHandlers = []; | ||
var oneTimers = []; | ||
var mostRecent = { | ||
response: null, | ||
navigation: null | ||
}; | ||
function respond(fn, options) { | ||
var _a = options || {}, _b = _a.observe, observe = _b === void 0 ? false : _b, _c = _a.initial, initial = _c === void 0 ? true : _c; | ||
if (observe) { | ||
var newLength_1 = responseHandlers.push(fn); | ||
if (mostRecent.response && initial) { | ||
fn.call(null, __assign({}, mostRecent, { router: router })); | ||
} | ||
return function () { | ||
responseHandlers[newLength_1 - 1] = null; | ||
}; | ||
} | ||
else { | ||
oneTimers.push(fn); | ||
if (mostRecent.response && initial) { | ||
fn.call(null, __assign({}, mostRecent, { router: router })); | ||
} | ||
else { | ||
oneTimers.push(fn); | ||
} | ||
} | ||
} | ||
} | ||
function emit(response, navigation) { | ||
var resp = { response: response, navigation: navigation, router: router }; | ||
beforeSideEffects.forEach(function (fn) { | ||
fn(resp); | ||
}); | ||
responseHandlers.forEach(function (fn) { | ||
if (fn != null) { | ||
function emit(response, navigation) { | ||
// store for current() and respond() | ||
mostRecent.response = response; | ||
mostRecent.navigation = navigation; | ||
var resp = { response: response, navigation: navigation, router: router }; | ||
beforeSideEffects.forEach(function (fn) { | ||
fn(resp); | ||
}); | ||
responseHandlers.forEach(function (fn) { | ||
if (fn != null) { | ||
fn(resp); | ||
} | ||
}); | ||
// calling one time responseHandlers after regular responseHandlers | ||
// ensures that those are called prior to the one time fns | ||
while (oneTimers.length) { | ||
var fn = oneTimers.pop(); | ||
if (fn) { | ||
fn(resp); | ||
} | ||
} | ||
}); | ||
// calling one time responseHandlers after regular responseHandlers | ||
// ensures that those are called prior to the one time fns | ||
while (oneTimers.length) { | ||
var fn = oneTimers.pop(); | ||
fn(resp); | ||
afterSideEffects.forEach(function (fn) { | ||
fn(resp); | ||
}); | ||
} | ||
afterSideEffects.forEach(function (fn) { | ||
fn(resp); | ||
}); | ||
} | ||
var activeResponse; | ||
function navigationHandler(pendingNav) { | ||
if (activeResponse) { | ||
activeResponse.cancel(pendingNav.action); | ||
activeResponse.cancelled = true; | ||
} | ||
activeResponse = pendingNav; | ||
var navigation = { | ||
action: pendingNav.action, | ||
previous: mostRecent.response | ||
}; | ||
if (cache) { | ||
var cachedResponse = cache.get(pendingNav.location); | ||
if (cachedResponse != null) { | ||
cacheAndEmit(cachedResponse, navigation); | ||
var activeResponse; | ||
function navigationHandler(pendingNav) { | ||
if (activeResponse) { | ||
activeResponse.cancel(pendingNav.action); | ||
activeResponse.cancelled = true; | ||
} | ||
} | ||
if (sync) { | ||
var pendingResponse = createResponse(pendingNav.location, routes); | ||
pendingNav.finish(); | ||
var response = finishResponse(pendingResponse, registeredAddons); | ||
cacheAndEmit(response, navigation); | ||
} | ||
else { | ||
asyncCreateResponse(pendingNav.location, routes).then(function (pendingResponse) { | ||
if (pendingNav.cancelled) { | ||
activeResponse = pendingNav; | ||
var navigation = { | ||
action: pendingNav.action, | ||
previous: mostRecent.response | ||
}; | ||
if (cache) { | ||
var cachedResponse = cache.get(pendingNav.location); | ||
if (cachedResponse != null) { | ||
cacheAndEmit(cachedResponse, navigation); | ||
return; | ||
} | ||
} | ||
var match = matchLocation(pendingNav.location, routes); | ||
// if no routes match, do nothing | ||
if (!match.route) { | ||
console.error("The current location (" + pendingNav.location.pathname + ") has no matching route, " + | ||
'so a response could not be generated. A catch-all route ({ path: "(.*)" }) ' + | ||
"can be used to match locations with no other matching route."); | ||
pendingNav.finish(); | ||
var response = finishResponse(pendingResponse, registeredAddons); | ||
return; | ||
} | ||
if (sync) { | ||
pendingNav.finish(); | ||
var response = finishResponse(match, registeredInteractions, null); | ||
cacheAndEmit(response, navigation); | ||
}); | ||
} | ||
else { | ||
resolveRoute(match).then(function (resolved) { | ||
if (pendingNav.cancelled) { | ||
return; | ||
} | ||
pendingNav.finish(); | ||
var response = finishResponse(match, registeredInteractions, resolved); | ||
cacheAndEmit(response, navigation); | ||
}); | ||
} | ||
} | ||
} | ||
function cacheAndEmit(response, navigation) { | ||
activeResponse = undefined; | ||
if (cache) { | ||
cache.set(response); | ||
function cacheAndEmit(response, navigation) { | ||
activeResponse = undefined; | ||
if (cache) { | ||
cache.set(response); | ||
} | ||
if (!response.redirectTo || emitRedirects) { | ||
emit(response, navigation); | ||
} | ||
if (response.redirectTo !== undefined) { | ||
history.replace(response.redirectTo); | ||
} | ||
} | ||
if (!response.redirectTo || emitRedirects) { | ||
mostRecent.response = response; | ||
mostRecent.navigation = navigation; | ||
emit(response, navigation); | ||
} | ||
if (response.redirectTo) { | ||
history.replace(response.redirectTo); | ||
} | ||
// now that everything is defined, actually do the setup | ||
setupRoutesAndInteractions(routeArray); | ||
history.respondWith(navigationHandler); | ||
var router = { | ||
route: registeredInteractions, | ||
history: history, | ||
respond: respond, | ||
replaceRoutes: setupRoutesAndInteractions, | ||
current: function () { | ||
return { | ||
response: mostRecent.response, | ||
navigation: mostRecent.navigation | ||
}; | ||
} | ||
}; | ||
return router; | ||
} | ||
// now that everything is defined, actually do the setup | ||
setupRoutesAndAddons(routeArray); | ||
history.respondWith(navigationHandler); | ||
var router = { | ||
addons: registeredAddons, | ||
history: history, | ||
respond: respond, | ||
refresh: setupRoutesAndAddons, | ||
current: function () { | ||
return { | ||
response: mostRecent.response, | ||
navigation: mostRecent.navigation | ||
}; | ||
} | ||
}; | ||
return router; | ||
} | ||
return createRouter; | ||
return createRouter; | ||
}()); |
@@ -1,1 +0,1 @@ | ||
var Curi=function(){"use strict";function e(r,n,t){r.forEach(function(r){var o=n.register(r.public,t);e(r.children,n,o)})}function r(e,r){for(var n,a=[],i=0,u=0,c="",s=r&&r.delimiter||P,l=r&&r.delimiters||q,p=!1;null!==(n=B.exec(e));){var f=n[0],h=n[1],v=n.index;if(c+=e.slice(u,v),u=v+f.length,h)c+=h[1],p=!0;else{var d="",m=e[u],g=n[2],y=n[3],x=n[4],b=n[5];if(!p&&c.length){var E=c.length-1;l.indexOf(c[E])>-1&&(d=c[E],c=c.slice(0,E))}c&&(a.push(c),c="",p=!1);var w=""!==d&&void 0!==m&&m!==d,T="+"===b||"*"===b,O="?"===b||"*"===b,j=d||s,A=y||x;a.push({name:g||i++,prefix:d,delimiter:j,optional:O,repeat:T,partial:w,pattern:A?o(A):"[^"+t(j)+"]+?"})}}return(c||u<e.length)&&a.push(c+e.substr(u)),a}function n(e){for(var r=new Array(e.length),n=0;n<e.length;n++)"object"==typeof e[n]&&(r[n]=new RegExp("^(?:"+e[n].pattern+")$"));return function(n,t){for(var o="",a=t&&t.encode||encodeURIComponent,i=0;i<e.length;i++){var u=e[i];if("string"!=typeof u){var c,s=n?n[u.name]:void 0;if(Array.isArray(s)){if(!u.repeat)throw new TypeError('Expected "'+u.name+'" to not repeat, but got array');if(0===s.length){if(u.optional)continue;throw new TypeError('Expected "'+u.name+'" to not be empty')}for(var l=0;l<s.length;l++){if(c=a(s[l],u),!r[i].test(c))throw new TypeError('Expected all "'+u.name+'" to match "'+u.pattern+'"');o+=(0===l?u.prefix:u.delimiter)+c}}else if("string"!=typeof s&&"number"!=typeof s&&"boolean"!=typeof s){if(!u.optional)throw new TypeError('Expected "'+u.name+'" to be '+(u.repeat?"an array":"a string"));u.partial&&(o+=u.prefix)}else{if(c=a(String(s),u),!r[i].test(c))throw new TypeError('Expected "'+u.name+'" to match "'+u.pattern+'", but got "'+c+'"');o+=u.prefix+c}}else o+=u}return o}}function t(e){return e.replace(/([.+*?=^!:${}()[\]|/\\])/g,"\\$1")}function o(e){return e.replace(/([=!:$/()])/g,"\\$1")}function a(e){return e&&e.sensitive?"":"i"}function i(e,r){if(!r)return e;var n=e.source.match(/\((?!\?)/g);if(n)for(var t=0;t<n.length;t++)r.push({name:t,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,pattern:null});return e}function u(e,r,n){for(var t=[],o=0;o<e.length;o++)t.push(l(e[o],r,n).source);return new RegExp("(?:"+t.join("|")+")",a(n))}function c(e,n,t){return s(r(e,t),n,t)}function s(e,r,n){for(var o=(n=n||{}).strict,i=!1!==n.end,u=t(n.delimiter||P),c=n.delimiters||q,s=[].concat(n.endsWith||[]).map(t).concat("$").join("|"),l="",p=!1,f=0;f<e.length;f++){var h=e[f];if("string"==typeof h)l+=t(h),p=f===e.length-1&&c.indexOf(h[h.length-1])>-1;else{var v=t(h.prefix),d=h.repeat?"(?:"+h.pattern+")(?:"+v+"(?:"+h.pattern+"))*":h.pattern;r&&r.push(h),h.optional?h.partial?l+=v+"("+d+")?":l+="(?:"+v+"("+d+"))?":l+=v+"("+d+")"}}return i?(o||(l+="(?:"+u+")?"),l+="$"===s?"$":"(?="+s+")"):(o||(l+="(?:"+u+"(?="+s+"))?"),p||(l+="(?="+u+"|"+s+")")),new RegExp("^"+l,a(n))}function l(e,r,n){return e instanceof RegExp?i(e,r):Array.isArray(e)?u(e,r,n):c(e,r,n)}function p(e){var r={},n={};return{name:"pathname",register:function(e,n){var t=e.name,o=e.path;void 0!==r[t]&&console.warn('A route with the name "'+t+'" already exists. Each route shouldhave a unique name. By registering a route with a name that already exists, you are overwriting the existing one. This may break your application.');var a;return n&&r[n]&&(a=r[n]),r[t]=a?I(a,o):o,t},get:function(t,o){if(null!=r[t]){var a=n[t]?n[t]:n[t]=A.compile(r[t]);return C(a(o,e))}console.error("Could not generate pathname for "+t+" because it is not registered.")},reset:function(){r={},n={}}}}function f(e,r){return{params:r.params,location:r.location,name:e.public.name}}function h(e,r){return{redirect:function(n){var t=n.name,o=n.params,a=n.query,i=n.hash,u=n.state,c=n.status,s=void 0===c?301:c;e.status=s;var l=r.pathname(t,o);e.redirectTo={pathname:l,query:a,hash:i,state:u}},error:function(r){e.error=r},status:function(r){e.status=r},data:function(r){e.data=r},body:function(r){e.body=r},title:function(r){e.title=r}}}function v(e,r){return Object.assign({key:r.location.key,name:e?e.public.name:void 0},r)}function d(e,r){var n=e.error,t=e.resolved,o=e.route,a=e.props;return o&&o.public.match.response&&o.public.match.response({error:n,resolved:t,route:f(o,a),set:h(a,r),addons:r}),v(o,a)}function m(e,r,n,t){var o=W(r),a=e.match,i=a.re,u=a.keys,c=a.mustBeExact,s=e.children,l=i.exec(o);if(!l)return!1;var p=l[0],f=l.slice(1),h={};u.forEach(function(e,r){h[e.name]=f[r]});var v=null!=t?I(t,p):C(p);if(n.push({route:e,params:h}),!s||!s.length)return!0;var d=o.slice(p.length),g=!!d.length,y=s.some(function(e){return m(e,d,n,v)});return!(c&&g&&!y)||(n.pop(),!1)}function g(e,r){if(!r)return e;var n={};for(var t in e){var o=e[t],a=r[t];if(a)try{o=a(o)}catch(r){console.error(r),o=e[t]}n[t]=o}return n}function y(e,r){var n,t=[],o=[],a={};if(r.some(function(r){return m(r,e.pathname,t)}),t.length){var i=t.pop();t.forEach(function(e){o.push(e.route.public.name),Object.assign(a,g(e.params,e.route.paramParsers))}),n=i.route,Object.assign(a,g(i.params,n.paramParsers))}return{route:n,props:{location:e,params:a,partials:o,status:null!=n?200:404,body:void 0,data:void 0,title:""}}}function x(e,r){return j({},y(e,r),{resolved:null,error:null})}function b(e,r){var n=y(e,r);return E(n.route,n.props)}function E(e,r){if(!e)return Promise.resolve({route:e,props:r});var n=e.public.match;return Promise.all([n.initial?n.initial():void 0,n.every?n.every(f(e,r)):void 0]).then(function(t){var o=t[0],a=t[1],i=n.initial||n.every?{initial:o,every:a}:null;return{route:e,props:r,error:null,resolved:i}},function(n){return{route:e,props:r,error:n,resolved:null}})}function w(e){var r=null,n=!1;return function(){return n?r:(r=e(),n=!0,r)}}function T(e){var r=e.public.match;return!(!r||!r.every&&!r.initial)}function O(e){return e.some(function(e){return!!T(e)||!!e.children.length&&O(e.children)})}var j=Object.assign||function(e){for(var r,n=1,t=arguments.length;n<t;n++){r=arguments[n];for(var o in r)Object.prototype.hasOwnProperty.call(r,o)&&(e[o]=r[o])}return e},A=l,k=r,R=n,$=s,P="/",q="./",B=new RegExp(["(\\\\.)","(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?"].join("|"),"g");A.parse=k,A.compile=function(e,t){return n(r(e,t))},A.tokensToFunction=R,A.tokensToRegExp=$;var C=function(e){return"/"===e.charAt(0)?e:"/"+e},W=function(e){return"/"===e.charAt(0)?e.slice(1):e},F=function(e){return"/"===e.charAt(e.length-1)?e:e+"/"},I=function(e,r){return F(e)+r},S=function(e){var r=e.name,n=e.path,t=e.pathOptions,o=void 0===t?{}:t,a=e.children,i=void 0===a?[]:a,u=e.match,c=void 0===u?{}:u,s=e.extra,l=e.params,p=null==o.end||o.end,f=[];i.length&&(o.end=!1,f=i.map(S));var h=[],v=A(n,h,o);return{public:{name:r,path:n,keys:h.map(function(e){return e.name}),match:{initial:c.initial&&w(c.initial),every:c.every,response:c.response},extra:s},match:{re:v,keys:h,mustBeExact:p},children:f,paramParsers:l}};return function(r,n,t){function o(r){A=r.map(S),y=!O(A);for(var n in k)delete k[n];R.forEach(function(r){r.reset(),k[r.name]=r.get,e(A,r)})}function a(e,r){var n={response:e,navigation:r,router:B};for(E.forEach(function(e){e(n)}),$.forEach(function(e){null!=e&&e(n)});P.length;)P.pop()(n);w.forEach(function(e){e(n)})}function i(e,n){T=void 0,h&&h.set(e),e.redirectTo&&!g||(q.response=e,q.navigation=n,a(e,n)),e.redirectTo&&r.replace(e.redirectTo)}void 0===t&&(t={});var u=t,c=u.addons,s=void 0===c?[]:c,l=u.sideEffects,f=void 0===l?[]:l,h=u.cache,v=u.pathnameOptions,m=u.emitRedirects,g=void 0===m||m,y=!0,E=[],w=[];f.forEach(function(e){e.after?w.push(e.fn):E.push(e.fn)});var T,A=[],k={},R=s.concat(p(v)),$=[],P=[],q={response:null,navigation:null};o(n),r.respondWith(function(e){T&&(T.cancel(e.action),T.cancelled=!0),T=e;var r={action:e.action,previous:q.response};if(h){var n=h.get(e.location);null!=n&&i(n,r)}if(y){var t=x(e.location,A);e.finish(),i(d(t,k),r)}else b(e.location,A).then(function(n){e.cancelled||(e.finish(),i(d(n,k),r))})});var B={addons:k,history:r,respond:function(e,r){var n=r||{},t=n.observe,o=void 0!==t&&t,a=n.initial,i=void 0===a||a;if(o){var u=$.push(e);return q.response&&i&&e.call(null,j({},q,{router:B})),function(){$[u-1]=null}}q.response&&i?e.call(null,j({},q,{router:B})):P.push(e)},refresh:o,current:function(){return{response:q.response,navigation:q.navigation}}};return B}}(); | ||
var Curi=function(){"use strict";var k=Object.assign||function(e){for(var r,n=1,t=arguments.length;n<t;n++)for(var a in r=arguments[n])Object.prototype.hasOwnProperty.call(r,a)&&(e[a]=r[a]);return e};var P=c,e=o,r=function(e,r){return a(o(e,r))},n=a,t=i,A="/",R="./",$=new RegExp(["(\\\\.)","(?:\\:(\\w+)(?:\\(((?:\\\\.|[^\\\\()])+)\\))?|\\(((?:\\\\.|[^\\\\()])+)\\))([+*?])?"].join("|"),"g");function o(e,r){for(var n,t,a=[],o=0,i=0,c="",u=r&&r.delimiter||A,l=r&&r.delimiters||R,s=!1;null!==(n=$.exec(e));){var p=n[0],f=n[1],h=n.index;if(c+=e.slice(i,h),i=h+p.length,f)c+=f[1],s=!0;else{var v="",m=e[i],d=n[2],g=n[3],y=n[4],b=n[5];if(!s&&c.length){var x=c.length-1;-1<l.indexOf(c[x])&&(v=c[x],c=c.slice(0,x))}c&&(a.push(c),c="",s=!1);var E=""!==v&&void 0!==m&&m!==v,w="+"===b||"*"===b,O="?"===b||"*"===b,T=v||u,j=g||y;a.push({name:d||o++,prefix:v,delimiter:T,optional:O,repeat:w,partial:E,pattern:j?(t=j,t.replace(/([=!:$/()])/g,"\\$1")):"[^"+M(T)+"]+?"})}}return(c||i<e.length)&&a.push(c+e.substr(i)),a}function a(l){for(var s=new Array(l.length),e=0;e<l.length;e++)"object"==typeof l[e]&&(s[e]=new RegExp("^(?:"+l[e].pattern+")$"));return function(e,r){for(var n="",t=r&&r.encode||encodeURIComponent,a=0;a<l.length;a++){var o=l[a];if("string"!=typeof o){var i,c=e?e[o.name]:void 0;if(Array.isArray(c)){if(!o.repeat)throw new TypeError('Expected "'+o.name+'" to not repeat, but got array');if(0===c.length){if(o.optional)continue;throw new TypeError('Expected "'+o.name+'" to not be empty')}for(var u=0;u<c.length;u++){if(i=t(c[u],o),!s[a].test(i))throw new TypeError('Expected all "'+o.name+'" to match "'+o.pattern+'"');n+=(0===u?o.prefix:o.delimiter)+i}}else if("string"!=typeof c&&"number"!=typeof c&&"boolean"!=typeof c){if(!o.optional)throw new TypeError('Expected "'+o.name+'" to be '+(o.repeat?"an array":"a string"));o.partial&&(n+=o.prefix)}else{if(i=t(String(c),o),!s[a].test(i))throw new TypeError('Expected "'+o.name+'" to match "'+o.pattern+'", but got "'+i+'"');n+=o.prefix+i}}else n+=o}return n}}function M(e){return e.replace(/([.+*?=^!:${}()[\]|/\\])/g,"\\$1")}function v(e){return e&&e.sensitive?"":"i"}function i(e,r,n){for(var t=(n=n||{}).strict,a=!1!==n.end,o=M(n.delimiter||A),i=n.delimiters||R,c=[].concat(n.endsWith||[]).map(M).concat("$").join("|"),u="",l=!1,s=0;s<e.length;s++){var p=e[s];if("string"==typeof p)u+=M(p),l=s===e.length-1&&-1<i.indexOf(p[p.length-1]);else{var f=M(p.prefix),h=p.repeat?"(?:"+p.pattern+")(?:"+f+"(?:"+p.pattern+"))*":p.pattern;r&&r.push(p),p.optional?p.partial?u+=f+"("+h+")?":u+="(?:"+f+"("+h+"))?":u+=f+"("+h+")"}}return a?(t||(u+="(?:"+o+")?"),u+="$"===c?"$":"(?="+c+")"):(t||(u+="(?:"+o+"(?="+c+"))?"),l||(u+="(?="+o+"|"+c+")")),new RegExp("^"+u,v(n))}function c(e,r,n){return e instanceof RegExp?function(e,r){if(!r)return e;var n=e.source.match(/\((?!\?)/g);if(n)for(var t=0;t<n.length;t++)r.push({name:t,prefix:null,delimiter:null,optional:!1,repeat:!1,partial:!1,pattern:null});return e}(e,r):Array.isArray(e)?function(e,r,n){for(var t=[],a=0;a<e.length;a++)t.push(c(e[a],r,n).source);return new RegExp("(?:"+t.join("|")+")",v(n))}(e,r,n):(t=r,i(o(e,a=n),t,a));var t,a}P.parse=e,P.compile=r,P.tokensToFunction=n,P.tokensToRegExp=t;var B=function(e){return"/"===e.charAt(0)?e:"/"+e},f=function(e){return"/"===e.charAt(0)?e.slice(1):e},C=function(e,r){return("/"===(n=e).charAt(n.length-1)?n:n+"/")+r;var n};function u(e,r){var n=e.name,t=e.params,a=function(e,r){var n={};for(var t in e)Object.prototype.hasOwnProperty.call(e,t)&&r.indexOf(t)<0&&(n[t]=e[t]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var a=0;for(t=Object.getOwnPropertySymbols(e);a<t.length;a++)r.indexOf(t[a])<0&&(n[t[a]]=e[t[a]])}return n}(e,["name","params"]),o=r.pathname(n,t);return k({pathname:o},a)}function S(e,r,n){var t=e.route,a=e.response;if(!t.response)return a;var o=t.response({resolved:n,name:a.name,params:k({},a.params),location:a.location,route:r});if(!o)return a;return["status","error","body","data","title","redirectTo"].forEach(function(e){o.hasOwnProperty(e)&&(a[e]="redirectTo"===e?u(o[e],r):o[e])}),a}function l(r,e){if(!e)return r;var n={};for(var t in r){var a=r[t],o=e[t];if(o)try{a=o(a)}catch(e){console.error(e),a=r[t]}n[t]=a}return n}function W(e,r){var n=f(r),t=e.pathMatching.re.exec(n);if(!t)return[];var a=t[0],o=t.slice(1),i={};e.pathMatching.keys.forEach(function(e,r){i[e.name]=o[r]});var c=[{route:e,params:i}];if(!e.children.length)return c;for(var u=n.slice(a.length),l=e.children.length,s=0;s<l;s++){var p=W(e.children[s],u);if(p.length){c=c.concat(p);break}}return 1<c.length||e.pathMatching.mustBeExact&&0===u.length?c:[]}function q(e,r){var n=[],t={},a=e.pop();return e.forEach(function(e){n.push(e.route.public.name),Object.assign(t,l(e.params,e.route.paramParsers))}),Object.assign(t,l(a.params,a.route.paramParsers)),{route:a.route,response:{location:r,key:r.key,name:a.route.public.name,params:t,partials:n,status:200,body:void 0,data:void 0,title:""}}}var F=function(e){var r=e.name,n=e.path,t=e.pathOptions,a=void 0===t?{}:t,o=e.children,i=void 0===o?[]:o,c=e.response,u=e.on,l=void 0===u?{}:u,s=e.extra,p=e.params,f=null==a.end||a.end,h=[];i.length&&(a.end=!1,h=i.map(F));var v,m,d,g=[],y=P(n,g,a);return{public:{name:r,path:n,keys:g.map(function(e){return e.name}),on:{initial:l.initial&&(v=l.initial,d=!1,function(){return d||(m=v(),d=!0),m}),every:l.every},extra:s},pathMatching:{re:y,keys:g,mustBeExact:f},response:c,children:h,paramParsers:p}};return function(n,e,r){void 0===r&&(r={});var t=r.route,a=void 0===t?[]:t,o=r.sideEffects,i=void 0===o?[]:o,c=r.cache,u=r.pathnameOptions,l=r.emitRedirects,s=void 0===l||l,p=!0,f=[],h=[];i.forEach(function(e){e.after?h.push(e.fn):f.push(e.fn)});var v,m,d,g=[],y={},b=a.concat((v=u,m={},d={},{name:"pathname",register:function(e,r){var n,t=e.name,a=e.path;return void 0!==m[t]&&console.warn('A route with the name "'+t+'" already exists. Each route shouldhave a unique name. By registering a route with a name that already exists, you are overwriting the existing one. This may break your application.'),r&&m[r]&&(n=m[r]),m[t]=n?C(n,a):a,t},get:function(e,r){if(null!=m[e]){var n=d[e]?d[e]:d[e]=P.compile(m[e]);return B(n(r,v))}console.error("Could not generate pathname for "+e+" because it is not registered.")},reset:function(){m={},d={}}}));function x(e){for(var r in g=e.map(F),p=!function n(e){return e.some(function(e){return!(!(r=e.public.on)||!r.every&&!r.initial)||!!e.children.length&&n(e.children);var r})}(g),y)delete y[r];b.forEach(function(e){e.reset(),y[e.name]=e.get,function n(e,t,a){e.forEach(function(e){var r=t.register(e.public,a);n(e.children,t,r)})}(g,e)})}var E,w=[],O=[],T={response:null,navigation:null};function j(e,r){E=void 0,c&&c.set(e),e.redirectTo&&!s||function(e,r){var n={response:T.response=e,navigation:T.navigation=r,router:A};for(f.forEach(function(e){e(n)}),w.forEach(function(e){null!=e&&e(n)});O.length;){var t=O.pop();t&&t(n)}h.forEach(function(e){e(n)})}(e,r),void 0!==e.redirectTo&&n.replace(e.redirectTo)}x(e),n.respondWith(function(r){E&&(E.cancel(r.action),E.cancelled=!0);var n={action:(E=r).action,previous:T.response};if(c){var e=c.get(r.location);if(null!=e)return void j(e,n)}var t,a,o,i=function(e,r){for(var n=r.length,t=0;t<n;t++){var a=W(r[t],e.pathname);if(a.length)return q(a,e)}return{route:void 0,response:void 0}}(r.location,g);if(!i.route)return console.error("The current location ("+r.location.pathname+') has no matching route, so a response could not be generated. A catch-all route ({ path: "(.*)" }) can be used to match locations with no other matching route.'),void r.finish();p?(r.finish(),j(S(i,y,null),n)):(t=i,a=t.response,o=t.route.public.on,Promise.all([o.initial&&o.initial(),o.every&&o.every({name:a.name,params:k({},a.params),location:a.location})]).then(function(e){return{error:null,initial:e[0],every:e[1]}},function(e){return{error:e,initial:null,every:null}})).then(function(e){r.cancelled||(r.finish(),j(S(i,y,e),n))})});var A={route:y,history:n,respond:function(e,r){var n=r||{},t=n.observe,a=void 0!==t&&t,o=n.initial,i=void 0===o||o;if(a){var c=w.push(e);return T.response&&i&&e.call(null,k({},T,{router:A})),function(){w[c-1]=null}}T.response&&i?e.call(null,k({},T,{router:A})):O.push(e)},replaceRoutes:x,current:function(){return{response:T.response,navigation:T.navigation}}};return A}}(); |
{ | ||
"name": "@curi/core", | ||
"version": "1.0.0-beta.28", | ||
"version": "1.0.0-beta.29", | ||
"description": "A JavaScript router that doesn't care how you render", | ||
@@ -46,3 +46,3 @@ "main": "dist/curi.common.js", | ||
"rimraf": "^2.6.2", | ||
"rollup": "^0.54.0", | ||
"rollup": "^0.58.1", | ||
"ts-jest": "^21.2.3", | ||
@@ -49,0 +49,0 @@ "typescript": "^2.6.1" |
@@ -1,3 +0,4 @@ | ||
import { Addons } from "./types/addon"; | ||
import { Response, PendingResponse } from "./types/response"; | ||
export default function finishResponse(pending: PendingResponse, addons: Addons): Response; | ||
import { Interactions } from "./types/interaction"; | ||
import { Response, Resolved } from "./types/response"; | ||
import { Match } from "./types/match"; | ||
export default function finishResponse(match: Match, interactions: Interactions, resolved: Resolved | null): Response; |
import { History, HickoryLocation, Action } from "@hickory/root"; | ||
import { PathFunctionOptions } from "path-to-regexp"; | ||
import { Addon, Addons } from "./addon"; | ||
import { Interaction, Interactions } from "./interaction"; | ||
import { RouteDescriptor } from "./route"; | ||
@@ -8,3 +8,3 @@ import { Response } from "./response"; | ||
action: Action; | ||
previous: Response; | ||
previous: Response | null; | ||
} | ||
@@ -31,3 +31,3 @@ export interface Emitted { | ||
export interface RouterOptions { | ||
addons?: Array<Addon>; | ||
route?: Array<Interaction>; | ||
sideEffects?: Array<SideEffect>; | ||
@@ -39,11 +39,11 @@ cache?: Cache; | ||
export interface CurrentResponse { | ||
response: Response; | ||
navigation: Navigation; | ||
response: Response | null; | ||
navigation: Navigation | null; | ||
} | ||
export interface CuriRouter { | ||
refresh: (routeArray: Array<RouteDescriptor>) => void; | ||
respond: (fn: ResponseHandler, options?: RespondOptions) => RemoveResponseHandler; | ||
addons: Addons; | ||
replaceRoutes: (routeArray: Array<RouteDescriptor>) => void; | ||
respond: (fn: ResponseHandler, options?: RespondOptions) => RemoveResponseHandler | void; | ||
route: Interactions; | ||
history: History; | ||
current(): CurrentResponse; | ||
} |
@@ -1,4 +0,4 @@ | ||
export { AddonRegister, AddonGet, Addon, Addons } from "./addon"; | ||
export { Route, RouteDescriptor, ParamParser, ParamParsers, RouteProps, ResponseSetters, ResponseBuilder, EveryMatchFn, InitialMatchFn, ResponseMatchFn } from "./route"; | ||
export { Response, RawParams, Params } from "./response"; | ||
export { RegisterInteraction, GetInteraction, Interaction, Interactions } from "./interaction"; | ||
export { Route, RouteDescriptor, ParamParser, ParamParsers, MatchedRouteProps, ResponseBuilder, EveryMatchFn, InitialMatchFn, OnFns } from "./route"; | ||
export { Response, RawParams, Params, ModifiableResponseProperties } from "./response"; | ||
export { CuriRouter, RouterOptions, ResponseHandler, Emitted, RemoveResponseHandler, SideEffect, Cache, Navigation } from "./curi"; |
@@ -1,3 +0,2 @@ | ||
import { HickoryLocation, ToArgument } from '@hickory/root'; | ||
import { InternalRoute } from './route'; | ||
import { HickoryLocation, ToArgument } from "@hickory/root"; | ||
export declare type RawParams = { | ||
@@ -9,35 +8,35 @@ [key: string]: string; | ||
}; | ||
export interface ResponseProps { | ||
export interface ModifiableResponseProperties { | ||
status?: number; | ||
error?: any; | ||
body?: any; | ||
data?: any; | ||
title?: string; | ||
redirectTo?: RedirectProps; | ||
} | ||
export interface GenericResponse { | ||
location: HickoryLocation; | ||
key: string; | ||
status: number; | ||
title: string; | ||
name: string; | ||
params: Params; | ||
partials: Array<string>; | ||
status: number; | ||
body: any; | ||
data: any; | ||
title: string; | ||
error?: any; | ||
redirectTo?: ToArgument; | ||
} | ||
export interface ResolvedObject { | ||
export declare type Response = GenericResponse; | ||
export interface Resolved { | ||
error: any; | ||
initial: any; | ||
every: any; | ||
} | ||
export interface PendingResponse { | ||
error?: any; | ||
resolved?: ResolvedObject; | ||
route: InternalRoute; | ||
props: ResponseProps; | ||
} | ||
export interface Response { | ||
key: string; | ||
location: HickoryLocation; | ||
status: number; | ||
data: any; | ||
title: string; | ||
body: any; | ||
name?: string; | ||
partials?: Array<string>; | ||
export interface RedirectProps { | ||
name: string; | ||
params?: Params; | ||
error?: any; | ||
redirectTo?: any; | ||
hash?: string; | ||
query?: any; | ||
state?: any; | ||
} |
import { RegExpOptions, Key } from "path-to-regexp"; | ||
import { LocationDetails } from "@hickory/root"; | ||
import { Params, ResponseProps } from './response'; | ||
import { Addons } from './addon'; | ||
import { Resolved, ResponseModifiers } from "./response"; | ||
import { Interactions } from "./interaction"; | ||
export declare type ParamParser = (input: string) => any; | ||
@@ -9,3 +8,3 @@ export interface ParamParsers { | ||
} | ||
export interface RouteProps { | ||
export interface MatchedRouteProps { | ||
params: object; | ||
@@ -15,29 +14,12 @@ location: object; | ||
} | ||
export interface RedirectProps extends LocationDetails { | ||
name: string; | ||
params?: Params; | ||
status?: number; | ||
export interface ResponseBuilder extends MatchedRouteProps { | ||
resolved: Resolved | null; | ||
route: Interactions; | ||
} | ||
export interface ResponseSetters { | ||
error: (err: any) => void; | ||
redirect: (props: RedirectProps) => void; | ||
data: (data: any) => void; | ||
status: (status: number) => void; | ||
body: (body: any) => void; | ||
title: (title: string) => void; | ||
} | ||
export interface ResponseBuilder { | ||
error: any; | ||
resolved: any; | ||
route: RouteProps; | ||
set: ResponseSetters; | ||
addons: Addons; | ||
} | ||
export declare type EveryMatchFn = (route?: RouteProps) => Promise<any>; | ||
export declare type EveryMatchFn = (matched?: MatchedRouteProps) => Promise<any>; | ||
export declare type InitialMatchFn = () => Promise<any>; | ||
export declare type ResponseMatchFn = (props: ResponseBuilder) => void; | ||
export interface MatchFns { | ||
export declare type ResponseFn = (props: ResponseBuilder) => ResponseModifiers; | ||
export interface OnFns { | ||
initial?: InitialMatchFn; | ||
every?: EveryMatchFn; | ||
response?: ResponseMatchFn; | ||
} | ||
@@ -50,3 +32,4 @@ export interface RouteDescriptor { | ||
children?: Array<RouteDescriptor>; | ||
match?: MatchFns; | ||
response?: ResponseFn; | ||
on?: OnFns; | ||
extra?: { | ||
@@ -60,8 +43,8 @@ [key: string]: any; | ||
keys: Array<string | number>; | ||
match: MatchFns; | ||
extra: { | ||
on: OnFns; | ||
extra?: { | ||
[key: string]: any; | ||
}; | ||
} | ||
export interface InternalMatch { | ||
export interface PathMatching { | ||
mustBeExact: boolean; | ||
@@ -74,12 +57,5 @@ re: RegExp; | ||
children: Array<InternalRoute>; | ||
match: InternalMatch; | ||
paramParsers: ParamParsers; | ||
response?: ResponseFn; | ||
pathMatching: PathMatching; | ||
paramParsers?: ParamParsers; | ||
} | ||
export interface Match { | ||
route: InternalRoute; | ||
params: Params; | ||
} | ||
export interface MatchedRoute { | ||
route: InternalRoute; | ||
props: ResponseProps; | ||
} |
@@ -1,2 +0,4 @@ | ||
import { InternalRoute, Match } from '../types/route'; | ||
export default function matchRoute(route: InternalRoute, pathname: string, matches: Array<Match>, parentPath?: string): boolean; | ||
import { HickoryLocation } from "@hickory/root"; | ||
import { InternalRoute } from "../types/route"; | ||
import { PossibleMatch } from "../types/match"; | ||
export default function matchLocation(location: HickoryLocation, routes: Array<InternalRoute>): PossibleMatch; |
@@ -1,3 +0,3 @@ | ||
import { ParamParsers } from '../types/route'; | ||
import { RawParams, Params } from '../types/response'; | ||
export default function parseParams(params: RawParams, fns: ParamParsers): Params; | ||
import { ParamParsers } from "../types/route"; | ||
import { RawParams, Params } from "../types/response"; | ||
export default function parseParams(params: RawParams, fns?: ParamParsers): Params; |
@@ -1,3 +0,3 @@ | ||
import { InternalRoute } from '../types/route'; | ||
import { Addon } from '../types/addon'; | ||
export default function registerRoutes(routes: Array<InternalRoute>, addon: Addon, parentData?: any): void; | ||
import { InternalRoute } from "../types/route"; | ||
import { Interaction } from "../types/interaction"; | ||
export default function registerRoutes(routes: Array<InternalRoute>, interaction: Interaction, parentData?: any): void; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
93577
1943