UNPKG

react-router

Version:

A complete routing library for React.js

278 lines (227 loc) • 8.73 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); exports.getState = getState; exports.createTransitionHook = createTransitionHook; exports.getTransitionHooks = getTransitionHooks; exports.getComponents = getComponents; exports.getRouteParams = getRouteParams; function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _slicedToArray(arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } } var _invariant = require('invariant'); var _invariant2 = _interopRequireDefault(_invariant); var _RouteUtils = require('./RouteUtils'); var _URLUtils = require('./URLUtils'); var _AsyncUtils = require('./AsyncUtils'); function getChildRoutes(route, locationState, callback) { if (route.childRoutes) { callback(null, route.childRoutes); } else if (route.getChildRoutes) { route.getChildRoutes(locationState, callback); } else { callback(); } } function getIndexRoute(route, locationState, callback) { if (route.indexRoute) { callback(null, route.indexRoute); } else if (route.getIndexRoute) { route.getIndexRoute(callback, locationState); } else { callback(); } } function assignParams(params, paramNames, paramValues) { return paramNames.reduceRight(function (params, paramName, index) { var paramValue = paramValues[index]; if (Array.isArray(params[paramName])) { params[paramName].unshift(paramValue); } else if (paramName in params) { params[paramName] = [paramValue, params[paramName]]; } else { params[paramName] = paramValue; } return params; }, params); } function createParams(paramNames, paramValues) { return assignParams({}, paramNames, paramValues); } function matchRouteDeep(route, pathname, locationState, callback) { var _matchPattern = (0, _URLUtils.matchPattern)(route.path, pathname); var remainingPathname = _matchPattern.remainingPathname; var paramNames = _matchPattern.paramNames; var paramValues = _matchPattern.paramValues; var isExactMatch = remainingPathname === ''; if (isExactMatch && route.path) { var params = createParams(paramNames, paramValues); var branch = [route]; getIndexRoute(route, locationState, function (error, indexRoute) { if (error) { callback(error); } else { if (indexRoute) branch.push(indexRoute); callback(null, { params: params, branch: branch }); } }); } else if (remainingPathname != null) { // This route matched at least some of the path. getChildRoutes(route, locationState, function (error, childRoutes) { if (error) { callback(error); } else if (childRoutes) { // Check the child routes to see if any of them match. matchRoutes(childRoutes, remainingPathname, locationState, function (error, match) { if (error) { callback(error); } else if (match) { // A child route matched! Augment the match and pass it up the stack. assignParams(match.params, paramNames, paramValues); match.branch.unshift(route); callback(null, match); } else { callback(); } }); } else { callback(); } }); } else { callback(); } } function matchRoutes(routes, pathname, locationState, callback) { routes = (0, _RouteUtils.createRoutes)(routes); (0, _AsyncUtils.loopAsync)(routes.length, function (index, next, done) { matchRouteDeep(routes[index], pathname, locationState, function (error, match) { if (error || match) { done(error, match); } else { next(); } }); }, callback); } /** * Asynchronously matches the given location to a set of routes and calls * callback(error, state) when finished. The state object may have the * following properties: * * - branch An array of routes that matched, in hierarchical order * - params An object of URL parameters * * Note: This operation may return synchronously if no routes have an * asynchronous getChildRoutes method. */ function getState(routes, location, callback) { matchRoutes(routes, (0, _URLUtils.stripLeadingSlashes)(location.pathname), location.state, callback); } function routeParamsChanged(route, prevState, nextState) { if (!route.path) return false; var paramNames = (0, _URLUtils.getParamNames)(route.path); return paramNames.some(function (paramName) { return prevState.params[paramName] !== nextState.params[paramName]; }); } /** * Runs a diff on the two router states and returns an array of two * arrays: 1) the routes that we are leaving, starting with the leaf * route and 2) the routes that we are entering, ending with the leaf * route. */ function computeDiff(prevState, nextState) { var fromRoutes = prevState && prevState.branch; var toRoutes = nextState.branch; var leavingRoutes, enteringRoutes; if (fromRoutes) { leavingRoutes = fromRoutes.filter(function (route) { return toRoutes.indexOf(route) === -1 || routeParamsChanged(route, prevState, nextState); }); // onLeave hooks start at the leaf route. leavingRoutes.reverse(); enteringRoutes = toRoutes.filter(function (route) { return fromRoutes.indexOf(route) === -1 || leavingRoutes.indexOf(route) !== -1; }); } else { leavingRoutes = []; enteringRoutes = toRoutes; } return [leavingRoutes, enteringRoutes]; } function createTransitionHook(fn, context) { return function (nextState, transition, callback) { if (fn.length > 2) { fn.call(context, nextState, transition, callback); } else { // Assume fn executes synchronously and // automatically call the callback for them. fn.call(context, nextState, transition); callback(); } }; } function getTransitionHooksFromRoutes(routes, hookName) { return routes.reduce(function (hooks, route) { if (route[hookName]) hooks.push(createTransitionHook(route[hookName], route)); return hooks; }, []); } /** * Compiles and returns an array of transition hook functions that * should be called before we transition to a new state. Transition * hook signatures are: * * - route.onLeave(nextState, transition[, callback ]) * - route.onEnter(nextState, transition[, callback ]) * * Transition hooks run in order from the leaf route in the branch * we're leaving, up the tree to the common parent route, and back * down the branch we're entering to the leaf route. * * If a transition hook needs to execute asynchronously it may have * a 3rd argument that it should call when it is finished. Otherwise * the transition executes synchronously. */ function getTransitionHooks(prevState, nextState) { var _computeDiff = computeDiff(prevState, nextState); var _computeDiff2 = _slicedToArray(_computeDiff, 2); var leavingRoutes = _computeDiff2[0]; var enteringRoutes = _computeDiff2[1]; var hooks = getTransitionHooksFromRoutes(leavingRoutes, 'onLeave'); hooks.push.apply(hooks, getTransitionHooksFromRoutes(enteringRoutes, 'onEnter')); return hooks; } function getComponentsForRoute(route, callback) { if (route.component || route.components) { callback(null, route.component || route.components); } else if (route.getComponents) { route.getComponents(callback); } else { callback(); } } /** * Asynchronously fetches all components needed for the given router * state and calls callback(error, components) when finished. * * Note: This operation may return synchronously if no routes have an * asynchronous getComponents method. */ function getComponents(routes, callback) { (0, _AsyncUtils.mapAsync)(routes, function (route, index, callback) { getComponentsForRoute(route, callback); }, callback); } /** * Extracts an object of params the given route cares about from * the given params object. */ function getRouteParams(route, params) { var routeParams = {}; if (!route.path) return routeParams; var paramNames = (0, _URLUtils.getParamNames)(route.path); for (var p in params) if (params.hasOwnProperty(p) && paramNames.indexOf(p) !== -1) routeParams[p] = params[p]; return routeParams; }