diff --git a/modules/History.js b/modules/History.js
index 107b4eeae5..4cb3d957cd 100644
--- a/modules/History.js
+++ b/modules/History.js
@@ -1,13 +1,18 @@
-import { history } from './PropTypes'
+import createPropTypes from './PropTypes'
-const History = {
+export default function createHistory(React) {
+ const { history } = createPropTypes(React)
- contextTypes: { history },
+ const History = {
+
+ contextTypes: { history },
+
+ componentWillMount () {
+ this.history = this.context.history
+ }
- componentWillMount () {
- this.history = this.context.history
}
-}
+ return History
-export default History
+}
diff --git a/modules/IndexLink.js b/modules/IndexLink.js
index 41971f5962..684b458c7f 100644
--- a/modules/IndexLink.js
+++ b/modules/IndexLink.js
@@ -1,12 +1,15 @@
-import React from 'react'
-import Link from './Link'
+import createLink from './Link'
-const IndexLink = React.createClass({
+export default function createIndexLink(React) {
+ const Link = createLink(React)
- render() {
- return
- }
+ const IndexLink = React.createClass({
-})
+ render() {
+ return
+ }
-export default IndexLink
+ })
+
+ return IndexLink
+}
diff --git a/modules/IndexRoute.js b/modules/IndexRoute.js
index a2402d83c6..5d1cbf6a6e 100644
--- a/modules/IndexRoute.js
+++ b/modules/IndexRoute.js
@@ -1,47 +1,52 @@
-import React from 'react'
import invariant from 'invariant'
import warning from 'warning'
-import { createRouteFromReactElement } from './RouteUtils'
-import { component, components, falsy } from './PropTypes'
-
-const { bool, func } = React.PropTypes
-
-/**
- * An is used to specify its parent's in
- * a JSX route config.
- */
-const IndexRoute = React.createClass({
-
- statics: {
-
- createRouteFromReactElement(element, parentRoute) {
- if (parentRoute) {
- parentRoute.indexRoute = createRouteFromReactElement(element)
- } else {
- warning(
- false,
- 'An does not make sense at the root of your route config'
- )
+import createRouteUtils from './RouteUtils'
+import createPropTypes from './PropTypes'
+
+export default function createIndexRoute(React) {
+ const { createRouteFromReactElement } = createRouteUtils(React)
+ const { component, components, falsy } = createPropTypes(React)
+
+ const { bool, func } = React.PropTypes
+
+ /**
+ * An is used to specify its parent's in
+ * a JSX route config.
+ */
+ const IndexRoute = React.createClass({
+
+ statics: {
+
+ createRouteFromReactElement(element, parentRoute) {
+ if (parentRoute) {
+ parentRoute.indexRoute = createRouteFromReactElement(element)
+ } else {
+ warning(
+ false,
+ 'An does not make sense at the root of your route config'
+ )
+ }
}
- }
-
- },
- propTypes: {
- path: falsy,
- ignoreScrollBehavior: bool,
- component,
- components,
- getComponents: func
- },
+ },
+
+ propTypes: {
+ path: falsy,
+ ignoreScrollBehavior: bool,
+ component,
+ components,
+ getComponents: func
+ },
+
+ render() {
+ invariant(
+ false,
+ ' elements are for router configuration only and should not be rendered'
+ )
+ }
- render() {
- invariant(
- false,
- ' elements are for router configuration only and should not be rendered'
- )
- }
+ })
-})
+ return IndexRoute
-export default IndexRoute
+}
diff --git a/modules/Lifecycle.js b/modules/Lifecycle.js
index 4738e3dc23..ac89b256f7 100644
--- a/modules/Lifecycle.js
+++ b/modules/Lifecycle.js
@@ -1,69 +1,72 @@
-import React from 'react'
import invariant from 'invariant'
-const { object } = React.PropTypes
+export default function createLifecycle(React) {
-/**
- * The Lifecycle mixin adds the routerWillLeave lifecycle method
- * to a component that may be used to cancel a transition or prompt
- * the user for confirmation.
- *
- * On standard transitions, routerWillLeave receives a single argument: the
- * location we're transitioning to. To cancel the transition, return false.
- * To prompt the user for confirmation, return a prompt message (string).
- *
- * routerWillLeave does not receive a location object during the beforeunload
- * event in web browsers (assuming you're using the useBeforeUnload history
- * enhancer). In this case, it is not possible for us to know the location
- * we're transitioning to so routerWillLeave must return a prompt message to
- * prevent the user from closing the tab.
- */
-const Lifecycle = {
+ const { object } = React.PropTypes
- propTypes: {
- // Route components receive the route object as a prop.
- route: object
- },
+ /**
+ * The Lifecycle mixin adds the routerWillLeave lifecycle method
+ * to a component that may be used to cancel a transition or prompt
+ * the user for confirmation.
+ *
+ * On standard transitions, routerWillLeave receives a single argument: the
+ * location we're transitioning to. To cancel the transition, return false.
+ * To prompt the user for confirmation, return a prompt message (string).
+ *
+ * routerWillLeave does not receive a location object during the beforeunload
+ * event in web browsers (assuming you're using the useBeforeUnload history
+ * enhancer). In this case, it is not possible for us to know the location
+ * we're transitioning to so routerWillLeave must return a prompt message to
+ * prevent the user from closing the tab.
+ */
+ const Lifecycle = {
- contextTypes: {
- history: object.isRequired,
- // Nested children receive the route as context, either
- // set by the route component using the RouteContext mixin
- // or by some other ancestor.
- route: object
- },
+ propTypes: {
+ // Route components receive the route object as a prop.
+ route: object
+ },
- _getRoute() {
- const route = this.props.route || this.context.route
+ contextTypes: {
+ history: object.isRequired,
+ // Nested children receive the route as context, either
+ // set by the route component using the RouteContext mixin
+ // or by some other ancestor.
+ route: object
+ },
- invariant(
- route,
- 'The Lifecycle mixin needs to be used either on 1) a or ' +
- '2) a descendant of a that uses the RouteContext mixin'
- )
+ _getRoute() {
+ const route = this.props.route || this.context.route
- return route
- },
+ invariant(
+ route,
+ 'The Lifecycle mixin needs to be used either on 1) a or ' +
+ '2) a descendant of a that uses the RouteContext mixin'
+ )
- componentWillMount() {
- invariant(
- this.routerWillLeave,
- 'The Lifecycle mixin requires you to define a routerWillLeave method'
- )
+ return route
+ },
- this.context.history.registerRouteHook(
- this._getRoute(),
- this.routerWillLeave
- )
- },
+ componentWillMount() {
+ invariant(
+ this.routerWillLeave,
+ 'The Lifecycle mixin requires you to define a routerWillLeave method'
+ )
+
+ this.context.history.registerRouteHook(
+ this._getRoute(),
+ this.routerWillLeave
+ )
+ },
+
+ componentWillUnmount() {
+ this.context.history.unregisterRouteHook(
+ this._getRoute(),
+ this.routerWillLeave
+ )
+ }
- componentWillUnmount() {
- this.context.history.unregisterRouteHook(
- this._getRoute(),
- this.routerWillLeave
- )
}
-}
+ return Lifecycle
-export default Lifecycle
+}
diff --git a/modules/Link.js b/modules/Link.js
index 06464a3e06..20c207c850 100644
--- a/modules/Link.js
+++ b/modules/Link.js
@@ -1,8 +1,5 @@
-import React from 'react'
import warning from 'warning'
-const { bool, object, string, func } = React.PropTypes
-
function isLeftClickEvent(event) {
return event.button === 0
}
@@ -19,100 +16,104 @@ function isEmptyObject(object) {
return true
}
-/**
- * A is used to create an element that links to a route.
- * When that route is active, the link gets an "active" class name
- * (or the value of its `activeClassName` prop).
- *
- * For example, assuming you have the following route:
- *
- *
- *
- * You could use the following component to link to that route:
- *
- *
- *
- * Links may pass along location state and/or query string parameters
- * in the state/query props, respectively.
- *
- *
- */
-const Link = React.createClass({
-
- contextTypes: {
- history: object
- },
-
- propTypes: {
- activeStyle: object,
- activeClassName: string,
- onlyActiveOnIndex: bool.isRequired,
- to: string.isRequired,
- query: object,
- state: object,
- onClick: func
- },
-
- getDefaultProps() {
- return {
- onlyActiveOnIndex: false,
- className: '',
- style: {}
- }
- },
+export default function createLink(React) {
+ const { bool, object, string, func } = React.PropTypes
+
+ /**
+ * A is used to create an element that links to a route.
+ * When that route is active, the link gets an "active" class name
+ * (or the value of its `activeClassName` prop).
+ *
+ * For example, assuming you have the following route:
+ *
+ *
+ *
+ * You could use the following component to link to that route:
+ *
+ *
+ *
+ * Links may pass along location state and/or query string parameters
+ * in the state/query props, respectively.
+ *
+ *
+ */
+ const Link = React.createClass({
+
+ contextTypes: {
+ history: object
+ },
+
+ propTypes: {
+ activeStyle: object,
+ activeClassName: string,
+ onlyActiveOnIndex: bool.isRequired,
+ to: string.isRequired,
+ query: object,
+ state: object,
+ onClick: func
+ },
+
+ getDefaultProps() {
+ return {
+ onlyActiveOnIndex: false,
+ className: '',
+ style: {}
+ }
+ },
- handleClick(event) {
- let allowTransition = true, clickResult
+ handleClick(event) {
+ let allowTransition = true, clickResult
- if (this.props.onClick)
- clickResult = this.props.onClick(event)
+ if (this.props.onClick)
+ clickResult = this.props.onClick(event)
- if (isModifiedEvent(event) || !isLeftClickEvent(event))
- return
+ if (isModifiedEvent(event) || !isLeftClickEvent(event))
+ return
- if (clickResult === false || event.defaultPrevented === true)
- allowTransition = false
+ if (clickResult === false || event.defaultPrevented === true)
+ allowTransition = false
- event.preventDefault()
+ event.preventDefault()
- if (allowTransition)
- this.context.history.pushState(this.props.state, this.props.to, this.props.query)
- },
+ if (allowTransition)
+ this.context.history.pushState(this.props.state, this.props.to, this.props.query)
+ },
- componentWillMount() {
- warning(
- this.context.history,
- 'A should not be rendered outside the context of history ' +
- 'some features including real hrefs, active styling, and navigation ' +
- 'will not function correctly'
- )
- },
+ componentWillMount() {
+ warning(
+ this.context.history,
+ 'A should not be rendered outside the context of history ' +
+ 'some features including real hrefs, active styling, and navigation ' +
+ 'will not function correctly'
+ )
+ },
- render() {
- const { history } = this.context
- const { activeClassName, activeStyle, onlyActiveOnIndex, to, query, state, onClick, ...props } = this.props
+ render() {
+ const { history } = this.context
+ const { activeClassName, activeStyle, onlyActiveOnIndex, to, query, state, onClick, ...props } = this.props
- props.onClick = this.handleClick
+ props.onClick = this.handleClick
- // Ignore if rendered outside the context
- // of history, simplifies unit testing.
- if (history) {
- props.href = history.createHref(to, query)
+ // Ignore if rendered outside the context
+ // of history, simplifies unit testing.
+ if (history) {
+ props.href = history.createHref(to, query)
- if (activeClassName || (activeStyle != null && !isEmptyObject(activeStyle))) {
- if (history.isActive(to, query, onlyActiveOnIndex)) {
- if (activeClassName)
- props.className += props.className === '' ? activeClassName : ` ${activeClassName}`
+ if (activeClassName || (activeStyle != null && !isEmptyObject(activeStyle))) {
+ if (history.isActive(to, query, onlyActiveOnIndex)) {
+ if (activeClassName)
+ props.className += props.className === '' ? activeClassName : ` ${activeClassName}`
- if (activeStyle)
- props.style = { ...props.style, ...activeStyle }
+ if (activeStyle)
+ props.style = { ...props.style, ...activeStyle }
+ }
}
}
- }
- return React.createElement('a', props)
- }
+ return React.createElement('a', props)
+ }
-})
+ })
-export default Link
+ return Link
+}
diff --git a/modules/PropTypes.js b/modules/PropTypes.js
index c22a9726b7..b6c5f0ad76 100644
--- a/modules/PropTypes.js
+++ b/modules/PropTypes.js
@@ -1,37 +1,40 @@
-import { PropTypes } from 'react'
+export default function createPropTypes(React) {
-const { func, object, arrayOf, oneOfType, element, shape, string } = PropTypes
+ const { func, object, arrayOf, oneOfType, element, shape, string } = React.PropTypes
-export function falsy(props, propName, componentName) {
- if (props[propName])
- return new Error(`<${componentName}> should not have a "${propName}" prop`)
-}
+ function falsy(props, propName, componentName) {
+ if (props[propName])
+ return new Error(`<${componentName}> should not have a "${propName}" prop`)
+ }
+
+ const history = shape({
+ listen: func.isRequired,
+ pushState: func.isRequired,
+ replaceState: func.isRequired,
+ go: func.isRequired
+ })
-export const history = shape({
- listen: func.isRequired,
- pushState: func.isRequired,
- replaceState: func.isRequired,
- go: func.isRequired
-})
+ const location = shape({
+ pathname: string.isRequired,
+ search: string.isRequired,
+ state: object,
+ action: string.isRequired,
+ key: string
+ })
-export const location = shape({
- pathname: string.isRequired,
- search: string.isRequired,
- state: object,
- action: string.isRequired,
- key: string
-})
+ const component = oneOfType([ func, string ])
+ const components = oneOfType([ component, object ])
+ const route = oneOfType([ object, element ])
+ const routes = oneOfType([ route, arrayOf(route) ])
-export const component = oneOfType([ func, string ])
-export const components = oneOfType([ component, object ])
-export const route = oneOfType([ object, element ])
-export const routes = oneOfType([ route, arrayOf(route) ])
+ return {
+ falsy,
+ history,
+ location,
+ component,
+ components,
+ route,
+ routes
+ }
-export default {
- falsy,
- history,
- location,
- component,
- components,
- route
}
diff --git a/modules/Redirect.js b/modules/Redirect.js
index e236566d98..2c44e0b3be 100644
--- a/modules/Redirect.js
+++ b/modules/Redirect.js
@@ -1,67 +1,73 @@
-import React from 'react'
import invariant from 'invariant'
-import { createRouteFromReactElement } from './RouteUtils'
+import createRouteUtils from './RouteUtils'
import { formatPattern } from './PatternUtils'
-import { falsy } from './PropTypes'
+import createPropTypes from './PropTypes'
-const { string, object } = React.PropTypes
+export default function createRedirect(React) {
+ const { createRouteFromReactElement } = createRouteUtils(React)
-/**
- * A is used to declare another URL path a client should be sent
- * to when they request a given URL.
- *
- * Redirects are placed alongside routes in the route configuration and are
- * traversed in the same manner.
- */
-const Redirect = React.createClass({
+ const { falsy } = createPropTypes(React)
- statics: {
+ const { string, object } = React.PropTypes
- createRouteFromReactElement(element) {
- const route = createRouteFromReactElement(element)
+ /**
+ * A is used to declare another URL path a client should be sent
+ * to when they request a given URL.
+ *
+ * Redirects are placed alongside routes in the route configuration and are
+ * traversed in the same manner.
+ */
+ const Redirect = React.createClass({
- if (route.from)
- route.path = route.from
+ statics: {
- // TODO: Handle relative pathnames, see #1658
- invariant(
- route.to.charAt(0) === '/',
- ' must be an absolute path. This should be fixed in the future'
- )
+ createRouteFromReactElement(element) {
+ const route = createRouteFromReactElement(element)
- route.onEnter = function (nextState, replaceState) {
- const { location, params } = nextState
- const pathname = route.to ? formatPattern(route.to, params) : location.pathname
+ if (route.from)
+ route.path = route.from
- replaceState(
- route.state || location.state,
- pathname,
- route.query || location.query
+ // TODO: Handle relative pathnames, see #1658
+ invariant(
+ route.to.charAt(0) === '/',
+ ' must be an absolute path. This should be fixed in the future'
)
+
+ route.onEnter = function (nextState, replaceState) {
+ const { location, params } = nextState
+ const pathname = route.to ? formatPattern(route.to, params) : location.pathname
+
+ replaceState(
+ route.state || location.state,
+ pathname,
+ route.query || location.query
+ )
+ }
+
+ return route
}
- return route
- }
+ },
- },
+ propTypes: {
+ path: string,
+ from: string, // Alias for path
+ to: string.isRequired,
+ query: object,
+ state: object,
+ onEnter: falsy,
+ children: falsy
+ },
- propTypes: {
- path: string,
- from: string, // Alias for path
- to: string.isRequired,
- query: object,
- state: object,
- onEnter: falsy,
- children: falsy
- },
+ render() {
+ invariant(
+ false,
+ ' elements are for router configuration only and should not be rendered'
+ )
+ }
- render() {
- invariant(
- false,
- ' elements are for router configuration only and should not be rendered'
- )
- }
+ })
-})
+ return Redirect
-export default Redirect
+}
diff --git a/modules/Route.js b/modules/Route.js
index 4eebbda44e..ea4334a3b4 100644
--- a/modules/Route.js
+++ b/modules/Route.js
@@ -1,59 +1,66 @@
-import React from 'react'
import warning from 'warning'
import invariant from 'invariant'
-import { createRouteFromReactElement } from './RouteUtils'
-import { component, components } from './PropTypes'
-
-const { string, bool, func } = React.PropTypes
-
-/**
- * A is used to declare which components are rendered to the page when
- * the URL matches a given pattern.
- *
- * Routes are arranged in a nested tree structure. When a new URL is requested,
- * the tree is searched depth-first to find a route whose path matches the URL.
- * When one is found, all routes in the tree that lead to it are considered
- * "active" and their components are rendered into the DOM, nested in the same
- * order as they are in the tree.
- */
-const Route = React.createClass({
-
- statics: {
-
- createRouteFromReactElement(element) {
- const route = createRouteFromReactElement(element)
-
- if (route.handler) {
- warning(
- false,
- ' is deprecated, use instead'
- )
-
- route.component = route.handler
- delete route.handler
+
+import createRouteUtils from './RouteUtils'
+import createPropTypes from './PropTypes'
+
+export default function createRoute(React) {
+
+ const { createRouteFromReactElement } = createRouteUtils(React)
+ const { component, components } = createPropTypes(React)
+
+ const { string, bool, func } = React.PropTypes
+
+ /**
+ * A is used to declare which components are rendered to the page when
+ * the URL matches a given pattern.
+ *
+ * Routes are arranged in a nested tree structure. When a new URL is requested,
+ * the tree is searched depth-first to find a route whose path matches the URL.
+ * When one is found, all routes in the tree that lead to it are considered
+ * "active" and their components are rendered into the DOM, nested in the same
+ * order as they are in the tree.
+ */
+ const Route = React.createClass({
+
+ statics: {
+
+ createRouteFromReactElement(element) {
+ const route = createRouteFromReactElement(element)
+
+ if (route.handler) {
+ warning(
+ false,
+ ' is deprecated, use instead'
+ )
+
+ route.component = route.handler
+ delete route.handler
+ }
+
+ return route
}
- return route
+ },
+
+ propTypes: {
+ path: string,
+ ignoreScrollBehavior: bool,
+ handler: component, // deprecated
+ component,
+ components,
+ getComponents: func
+ },
+
+ render() {
+ invariant(
+ false,
+ ' elements are for router configuration only and should not be rendered'
+ )
}
-
- },
-
- propTypes: {
- path: string,
- ignoreScrollBehavior: bool,
- handler: component, // deprecated
- component,
- components,
- getComponents: func
- },
-
- render() {
- invariant(
- false,
- ' elements are for router configuration only and should not be rendered'
- )
- }
-
-})
-
-export default Route
+
+ })
+
+ return Route
+
+}
diff --git a/modules/RouteContext.js b/modules/RouteContext.js
index 0483df1769..7f9202a580 100644
--- a/modules/RouteContext.js
+++ b/modules/RouteContext.js
@@ -1,29 +1,31 @@
-import React from 'react'
+export default function createRouteContext(React) {
-const { object } = React.PropTypes
+ const { object } = React.PropTypes
-/**
- * The RouteContext mixin provides a convenient way for route
- * components to set the route in context. This is needed for
- * routes that render elements that want to use the Lifecycle
- * mixin to prevent transitions.
- */
-const RouteContext = {
+ /**
+ * The RouteContext mixin provides a convenient way for route
+ * components to set the route in context. This is needed for
+ * routes that render elements that want to use the Lifecycle
+ * mixin to prevent transitions.
+ */
+ const RouteContext = {
- propTypes: {
- route: object.isRequired
- },
+ propTypes: {
+ route: object.isRequired
+ },
- childContextTypes: {
- route: object.isRequired
- },
+ childContextTypes: {
+ route: object.isRequired
+ },
- getChildContext() {
- return {
- route: this.props.route
+ getChildContext() {
+ return {
+ route: this.props.route
+ }
}
+
}
-}
+ return RouteContext
-export default RouteContext
+}
diff --git a/modules/RouteUtils.js b/modules/RouteUtils.js
index 9b1da7a9ef..b461ba8e81 100644
--- a/modules/RouteUtils.js
+++ b/modules/RouteUtils.js
@@ -1,97 +1,107 @@
-import React from 'react'
import warning from 'warning'
-function isValidChild(object) {
- return object == null || React.isValidElement(object)
-}
+export default function createRouteUtils(React) {
-export function isReactChildren(object) {
- return isValidChild(object) || (Array.isArray(object) && object.every(isValidChild))
-}
+ function isValidChild(object) {
+ return object == null || React.isValidElement(object)
+ }
+
+ function isReactChildren(object) {
+ return isValidChild(object) || (Array.isArray(object) && object.every(isValidChild))
+ }
-function checkPropTypes(componentName, propTypes, props) {
- componentName = componentName || 'UnknownComponent'
+ function checkPropTypes(componentName, propTypes, props) {
+ componentName = componentName || 'UnknownComponent'
- for (const propName in propTypes) {
- if (propTypes.hasOwnProperty(propName)) {
- const error = propTypes[propName](props, propName, componentName)
+ for (const propName in propTypes) {
+ if (propTypes.hasOwnProperty(propName)) {
+ const error = propTypes[propName](props, propName, componentName)
- if (error instanceof Error)
- warning(false, error.message)
+ if (error instanceof Error)
+ warning(false, error.message)
+ }
}
}
-}
-function createRoute(defaultProps, props) {
- return { ...defaultProps, ...props }
-}
+ function createRoute(defaultProps, props) {
+ return { ...defaultProps, ...props }
+ }
-export function createRouteFromReactElement(element) {
- const type = element.type
- const route = createRoute(type.defaultProps, element.props)
+ function createRouteFromReactElement(element) {
+ const type = element.type
+ const route = createRoute(type.defaultProps, element.props)
- if (type.propTypes)
- checkPropTypes(type.displayName || type.name, type.propTypes, route)
+ if (type.propTypes)
+ checkPropTypes(type.displayName || type.name, type.propTypes, route)
- if (route.children) {
- const childRoutes = createRoutesFromReactChildren(route.children, route)
+ if (route.children) {
+ const childRoutes = createRoutesFromReactChildren(route.children, route)
- if (childRoutes.length)
- route.childRoutes = childRoutes
+ if (childRoutes.length)
+ route.childRoutes = childRoutes
- delete route.children
- }
+ delete route.children
+ }
- return route
-}
+ return route
+ }
-/**
- * Creates and returns a routes object from the given ReactChildren. JSX
- * provides a convenient way to visualize how routes in the hierarchy are
- * nested.
- *
- * import { Route, createRoutesFromReactChildren } from 'react-router'
- *
- * const routes = createRoutesFromReactChildren(
- *
- *
- *
- *
- * )
- *
- * Note: This method is automatically used when you provide children
- * to a component.
- */
-export function createRoutesFromReactChildren(children, parentRoute) {
- const routes = []
-
- React.Children.forEach(children, function (element) {
- if (React.isValidElement(element)) {
- // Component classes may have a static create* method.
- if (element.type.createRouteFromReactElement) {
- const route = element.type.createRouteFromReactElement(element, parentRoute)
-
- if (route)
- routes.push(route)
- } else {
- routes.push(createRouteFromReactElement(element))
+ /**
+ * Creates and returns a routes object from the given ReactChildren. JSX
+ * provides a convenient way to visualize how routes in the hierarchy are
+ * nested.
+ *
+ * import { Route, createRoutesFromReactChildren } from 'react-router'
+ *
+ * const routes = createRoutesFromReactChildren(
+ *
+ *
+ *
+ *
+ * )
+ *
+ * Note: This method is automatically used when you provide children
+ * to a component.
+ */
+ function createRoutesFromReactChildren(children, parentRoute) {
+ const routes = []
+
+ React.Children.forEach(children, function (element) {
+ if (React.isValidElement(element)) {
+ // Component classes may have a static create* method.
+ if (element.type.createRouteFromReactElement) {
+ const route = element.type.createRouteFromReactElement(element, parentRoute)
+
+ if (route)
+ routes.push(route)
+ } else {
+ routes.push(createRouteFromReactElement(element))
+ }
}
+ })
+
+ return routes
+ }
+
+ /**
+ * Creates and returns an array of routes from the given object which
+ * may be a JSX route, a plain object route, or an array of either.
+ */
+ function createRoutes(routes) {
+ if (isReactChildren(routes)) {
+ routes = createRoutesFromReactChildren(routes)
+ } else if (!Array.isArray(routes)) {
+ routes = [ routes ]
}
- })
- return routes
-}
+ return routes
+ }
-/**
- * Creates and returns an array of routes from the given object which
- * may be a JSX route, a plain object route, or an array of either.
- */
-export function createRoutes(routes) {
- if (isReactChildren(routes)) {
- routes = createRoutesFromReactChildren(routes)
- } else if (!Array.isArray(routes)) {
- routes = [ routes ]
+ return {
+ isReactChildren,
+ createRouteFromReactElement,
+ createRoutesFromReactChildren,
+ createRoutes
}
- return routes
}
diff --git a/modules/Router.js b/modules/Router.js
index 0598724b24..915d6dcb62 100644
--- a/modules/Router.js
+++ b/modules/Router.js
@@ -1,97 +1,102 @@
-import React from 'react'
import warning from 'warning'
import createHashHistory from 'history/lib/createHashHistory'
-import { createRoutes } from './RouteUtils'
-import RoutingContext from './RoutingContext'
-import useRoutes from './useRoutes'
-import { routes } from './PropTypes'
+import createRouteUtils from './RouteUtils'
+import createRoutingContext from './RoutingContext'
+import createUseRoutes from './useRoutes'
+import createPropTypes from './PropTypes'
-const { func, object } = React.PropTypes
+export default function createRouter(React) {
+ const { createRoutes } = createRouteUtils(React)
+ const RoutingContext = createRoutingContext(React)
+ const useRoutes = createUseRoutes(React)
+ const { routes } = createPropTypes(React)
+ const { func, object } = React.PropTypes
-/**
- * A is a high-level API for automatically setting up
- * a router that renders a with all the props
- * it needs each time the URL changes.
- */
-const Router = React.createClass({
+ /**
+ * A is a high-level API for automatically setting up
+ * a router that renders a with all the props
+ * it needs each time the URL changes.
+ */
+ const Router = React.createClass({
- propTypes: {
- history: object,
- children: routes,
- routes, // alias for children
- createElement: func,
- onError: func,
- onUpdate: func,
- parseQueryString: func,
- stringifyQuery: func
- },
+ propTypes: {
+ history: object,
+ children: routes,
+ routes, // alias for children
+ createElement: func,
+ onError: func,
+ onUpdate: func,
+ parseQueryString: func,
+ stringifyQuery: func
+ },
- getInitialState() {
- return {
- location: null,
- routes: null,
- params: null,
- components: null
- }
- },
+ getInitialState() {
+ return {
+ location: null,
+ routes: null,
+ params: null,
+ components: null
+ }
+ },
- handleError(error) {
- if (this.props.onError) {
- this.props.onError.call(this, error)
- } else {
- // Throw errors by default so we don't silently swallow them!
- throw error // This error probably occurred in getChildRoutes or getComponents.
- }
- },
+ handleError(error) {
+ if (this.props.onError) {
+ this.props.onError.call(this, error)
+ } else {
+ // Throw errors by default so we don't silently swallow them!
+ throw error // This error probably occurred in getChildRoutes or getComponents.
+ }
+ },
- componentWillMount() {
- let { history, children, routes, parseQueryString, stringifyQuery } = this.props
- let createHistory = history ? () => history : createHashHistory
+ componentWillMount() {
+ let { history, children, routes, parseQueryString, stringifyQuery } = this.props
+ let createHistory = history ? () => history : createHashHistory
- this.history = useRoutes(createHistory)({
- routes: createRoutes(routes || children),
- parseQueryString,
- stringifyQuery
- })
+ this.history = useRoutes(createHistory)({
+ routes: createRoutes(routes || children),
+ parseQueryString,
+ stringifyQuery
+ })
- this._unlisten = this.history.listen((error, state) => {
- if (error) {
- this.handleError(error)
- } else {
- this.setState(state, this.props.onUpdate)
- }
- })
- },
+ this._unlisten = this.history.listen((error, state) => {
+ if (error) {
+ this.handleError(error)
+ } else {
+ this.setState(state, this.props.onUpdate)
+ }
+ })
+ },
- componentWillReceiveProps(nextProps) {
- warning(
- nextProps.history === this.props.history,
- "The `history` provided to has changed, it will be ignored."
- )
- },
+ componentWillReceiveProps(nextProps) {
+ warning(
+ nextProps.history === this.props.history,
+ "The `history` provided to has changed, it will be ignored."
+ )
+ },
- componentWillUnmount() {
- if (this._unlisten)
- this._unlisten()
- },
+ componentWillUnmount() {
+ if (this._unlisten)
+ this._unlisten()
+ },
- render() {
- let { location, routes, params, components } = this.state
- let { createElement } = this.props
+ render() {
+ let { location, routes, params, components } = this.state
+ let { createElement } = this.props
- if (location == null)
- return null // Async match
+ if (location == null)
+ return null // Async match
- return React.createElement(RoutingContext, {
- history: this.history,
- createElement,
- location,
- routes,
- params,
- components
- })
- }
+ return React.createElement(RoutingContext, {
+ history: this.history,
+ createElement,
+ location,
+ routes,
+ params,
+ components
+ })
+ }
-})
+ })
-export default Router
+ return Router
+}
diff --git a/modules/RoutingContext.js b/modules/RoutingContext.js
index fbe9f4cae6..c8b4e5f6e1 100644
--- a/modules/RoutingContext.js
+++ b/modules/RoutingContext.js
@@ -1,91 +1,94 @@
-import React from 'react'
import invariant from 'invariant'
import getRouteParams from './getRouteParams'
-const { array, func, object } = React.PropTypes
-
-/**
- * A renders the component tree for a given router state
- * and sets the history object and the current location in context.
- */
-const RoutingContext = React.createClass({
-
- propTypes: {
- history: object.isRequired,
- createElement: func.isRequired,
- location: object.isRequired,
- routes: array.isRequired,
- params: object.isRequired,
- components: array.isRequired
- },
-
- getDefaultProps() {
- return {
- createElement: React.createElement
+export default function createRoutingContext(React) {
+
+ const { array, func, object } = React.PropTypes
+
+ /**
+ * A renders the component tree for a given router state
+ * and sets the history object and the current location in context.
+ */
+ const RoutingContext = React.createClass({
+
+ propTypes: {
+ history: object.isRequired,
+ createElement: func.isRequired,
+ location: object.isRequired,
+ routes: array.isRequired,
+ params: object.isRequired,
+ components: array.isRequired
+ },
+
+ getDefaultProps() {
+ return {
+ createElement: React.createElement
+ }
+ },
+
+ childContextTypes: {
+ history: object.isRequired,
+ location: object.isRequired
+ },
+
+ getChildContext() {
+ return {
+ history: this.props.history,
+ location: this.props.location
+ }
+ },
+
+ createElement(component, props) {
+ return component == null ? null : this.props.createElement(component, props)
+ },
+
+ render() {
+ const { history, location, routes, params, components } = this.props
+ let element = null
+
+ if (components) {
+ element = components.reduceRight((element, components, index) => {
+ if (components == null)
+ return element // Don't create new children use the grandchildren.
+
+ const route = routes[index]
+ const routeParams = getRouteParams(route, params)
+ const props = {
+ history,
+ location,
+ params,
+ route,
+ routeParams,
+ routes
+ }
+
+ if (element)
+ props.children = element
+
+ if (typeof components === 'object') {
+ const elements = {}
+
+ for (const key in components)
+ if (components.hasOwnProperty(key))
+ elements[key] = this.createElement(components[key], props)
+
+ return elements
+ }
+
+ return this.createElement(components, props)
+ }, element)
+ }
+
+ invariant(
+ element === null || element === false || React.isValidElement(element),
+ 'The root route must render a single element'
+ )
+
+ return element
}
- },
- childContextTypes: {
- history: object.isRequired,
- location: object.isRequired
- },
+ })
- getChildContext() {
- return {
- history: this.props.history,
- location: this.props.location
- }
- },
-
- createElement(component, props) {
- return component == null ? null : this.props.createElement(component, props)
- },
-
- render() {
- const { history, location, routes, params, components } = this.props
- let element = null
-
- if (components) {
- element = components.reduceRight((element, components, index) => {
- if (components == null)
- return element // Don't create new children use the grandchildren.
-
- const route = routes[index]
- const routeParams = getRouteParams(route, params)
- const props = {
- history,
- location,
- params,
- route,
- routeParams,
- routes
- }
-
- if (element)
- props.children = element
-
- if (typeof components === 'object') {
- const elements = {}
-
- for (const key in components)
- if (components.hasOwnProperty(key))
- elements[key] = this.createElement(components[key], props)
-
- return elements
- }
-
- return this.createElement(components, props)
- }, element)
- }
-
- invariant(
- element === null || element === false || React.isValidElement(element),
- 'The root route must render a single element'
- )
-
- return element
- }
-
-})
+ return RoutingContext
-export default RoutingContext
+}
diff --git a/modules/__tests__/History-test.js b/modules/__tests__/History-test.js
index b77d8ebe67..7d996399e3 100644
--- a/modules/__tests__/History-test.js
+++ b/modules/__tests__/History-test.js
@@ -1,10 +1,15 @@
/*eslint-env mocha */
import expect from 'expect'
import React from 'react'
-import History from '../History'
-import Router from '../Router'
-import Route from '../Route'
-import createHistory from 'history/lib/createMemoryHistory'
+import createHistory from '../History'
+import createRouter from '../Router'
+import createRoute from '../Route'
+import createMemoryHistory from 'history/lib/createMemoryHistory'
+
+const History = createHistory(React)
+const Router = createRouter(React)
+const Route = createRoute(React)
+
describe('History Mixin', function () {
@@ -18,7 +23,7 @@ describe('History Mixin', function () {
})
it('assigns the history to the component instance', function (done) {
- let history = createHistory('/')
+ let history = createMemoryHistory('/')
function assertHistory() {
expect(this.history).toExist()
diff --git a/modules/__tests__/IndexRoute-test.js b/modules/__tests__/IndexRoute-test.js
index 0759f9a7bd..c5669e0620 100644
--- a/modules/__tests__/IndexRoute-test.js
+++ b/modules/__tests__/IndexRoute-test.js
@@ -3,9 +3,13 @@
import expect from 'expect'
import React from 'react'
import createHistory from 'history/lib/createMemoryHistory'
-import IndexRoute from '../IndexRoute'
-import Router from '../Router'
-import Route from '../Route'
+import createIndexRoute from '../IndexRoute'
+import createRouter from '../Router'
+import createRoute from '../Route'
+
+const IndexRoute = createIndexRoute(React)
+const Router = createRouter(React)
+const Route = createRoute(React)
describe('an ', function () {
diff --git a/modules/__tests__/Link-test.js b/modules/__tests__/Link-test.js
index 2156dc5c9e..4bb4249220 100644
--- a/modules/__tests__/Link-test.js
+++ b/modules/__tests__/Link-test.js
@@ -5,9 +5,13 @@ import expect from 'expect'
import React from 'react/addons'
import createHistory from 'history/lib/createMemoryHistory'
import execSteps from './execSteps'
-import Router from '../Router'
-import Route from '../Route'
-import Link from '../Link'
+import createRouter from '../Router'
+import createRoute from '../Route'
+import createLink from '../Link'
+
+const Router = createRouter(React)
+const Route = createRoute(React)
+const Link = createLink(React)
const { click } = React.addons.TestUtils.Simulate
diff --git a/modules/__tests__/Redirect-test.js b/modules/__tests__/Redirect-test.js
index 0d7f3e5081..d208fee8e6 100644
--- a/modules/__tests__/Redirect-test.js
+++ b/modules/__tests__/Redirect-test.js
@@ -2,9 +2,13 @@
import expect from 'expect'
import React from 'react'
import createHistory from 'history/lib/createMemoryHistory'
-import Redirect from '../Redirect'
-import Router from '../Router'
-import Route from '../Route'
+import createRedirect from '../Redirect'
+import createRouter from '../Router'
+import createRoute from '../Route'
+
+const Redirect = createRedirect(React)
+const Router = createRouter(React)
+const Route = createRoute(React)
describe('A ', function () {
diff --git a/modules/__tests__/RouteComponent-test.js b/modules/__tests__/RouteComponent-test.js
index 159bd26148..2a323e87d1 100644
--- a/modules/__tests__/RouteComponent-test.js
+++ b/modules/__tests__/RouteComponent-test.js
@@ -2,7 +2,9 @@
import expect from 'expect'
import React from 'react'
import createHistory from 'history/lib/createMemoryHistory'
-import Router from '../Router'
+import createRouter from '../Router'
+
+const Router = createRouter(React)
describe('a Route Component', function () {
diff --git a/modules/__tests__/Router-test.js b/modules/__tests__/Router-test.js
index d9648c3177..6ec52f81b8 100644
--- a/modules/__tests__/Router-test.js
+++ b/modules/__tests__/Router-test.js
@@ -3,8 +3,11 @@
import expect from 'expect'
import React from 'react'
import createHistory from 'history/lib/createMemoryHistory'
-import Router from '../Router'
-import Route from '../Route'
+import createRouter from '../Router'
+import createRoute from '../Route'
+
+const Router = createRouter(React)
+const Route = createRoute(React)
describe('Router', function () {
diff --git a/modules/__tests__/createRoutesFromReactChildren-test.js b/modules/__tests__/createRoutesFromReactChildren-test.js
index a4730014c6..719702dc28 100644
--- a/modules/__tests__/createRoutesFromReactChildren-test.js
+++ b/modules/__tests__/createRoutesFromReactChildren-test.js
@@ -2,9 +2,13 @@
/*eslint react/prop-types: 0*/
import expect from 'expect'
import React from 'react'
-import { createRoutesFromReactChildren } from '../RouteUtils'
-import IndexRoute from '../IndexRoute'
-import Route from '../Route'
+import createRouteUtils from '../RouteUtils'
+import createIndexRoute from '../IndexRoute'
+import createRoute from '../Route'
+
+const Route = createRoute(React)
+const IndexRoute = createIndexRoute(React)
+const { createRoutesFromReactChildren } = createRouteUtils(React)
describe('createRoutesFromReactChildren', function () {
diff --git a/modules/__tests__/isActive-test.js b/modules/__tests__/isActive-test.js
index c435fae244..d9f2d561be 100644
--- a/modules/__tests__/isActive-test.js
+++ b/modules/__tests__/isActive-test.js
@@ -2,9 +2,13 @@
import expect from 'expect'
import React from 'react'
import createHistory from 'history/lib/createMemoryHistory'
-import IndexRoute from '../IndexRoute'
-import Router from '../Router'
-import Route from '../Route'
+import createIndexRoute from '../IndexRoute'
+import createRouter from '../Router'
+import createRoute from '../Route'
+
+const IndexRoute = createIndexRoute(React)
+const Router = createRouter(React)
+const Route = createRoute(React)
describe('isActive', function () {
diff --git a/modules/__tests__/matchRoutes-test.js b/modules/__tests__/matchRoutes-test.js
index f318fe672e..396a7cd5da 100644
--- a/modules/__tests__/matchRoutes-test.js
+++ b/modules/__tests__/matchRoutes-test.js
@@ -1,12 +1,16 @@
/*eslint-env mocha */
import React from 'react'
-import Route from '../Route'
+import createRoute from '../Route'
import assert from 'assert'
import expect from 'expect'
import { createLocation } from 'history'
-import { createRoutes } from '../RouteUtils'
-import matchRoutes from '../matchRoutes'
+import createRouteUtils from '../RouteUtils'
+import createMatchRoutes from '../matchRoutes'
+
+const Route = createRoute(React)
+const { createRoutes } = createRouteUtils(React)
+const matchRoutes = createMatchRoutes(React)
describe('matchRoutes', function () {
diff --git a/modules/__tests__/pushState-test.js b/modules/__tests__/pushState-test.js
index 7b311c7c3f..27da7ecc77 100644
--- a/modules/__tests__/pushState-test.js
+++ b/modules/__tests__/pushState-test.js
@@ -3,8 +3,11 @@ import expect from 'expect'
import React from 'react'
import resetHash from './resetHash'
import execSteps from './execSteps'
-import Router from '../Router'
-import Route from '../Route'
+import createRouter from '../Router'
+import createRoute from '../Route'
+
+const Router = createRouter(React)
+const Route = createRoute(React)
describe('pushState', function () {
beforeEach(resetHash)
diff --git a/modules/__tests__/serverRendering-test.js b/modules/__tests__/serverRendering-test.js
index 2e74d79cfd..675b70576b 100644
--- a/modules/__tests__/serverRendering-test.js
+++ b/modules/__tests__/serverRendering-test.js
@@ -3,9 +3,13 @@
import expect from 'expect'
import React from 'react'
import createLocation from 'history/lib/createLocation'
-import RoutingContext from '../RoutingContext'
-import match from '../match'
-import Link from '../Link'
+import createRoutingContext from '../RoutingContext'
+import createMatch from '../match'
+import createLink from '../Link'
+
+const RoutingContext = createRoutingContext(React)
+const match = createMatch(React)
+const Link = createLink(React)
describe('server rendering', function () {
diff --git a/modules/__tests__/transitionHooks-test.js b/modules/__tests__/transitionHooks-test.js
index 6e998ab188..619effdd3c 100644
--- a/modules/__tests__/transitionHooks-test.js
+++ b/modules/__tests__/transitionHooks-test.js
@@ -4,7 +4,9 @@ import expect, { spyOn } from 'expect'
import React from 'react'
import createHistory from 'history/lib/createMemoryHistory'
import execSteps from './execSteps'
-import Router from '../Router'
+import createRouter from '../Router'
+
+const Router = createRouter(React)
describe('When a router enters a branch', function () {
diff --git a/modules/createAll.js b/modules/createAll.js
new file mode 100644
index 0000000000..6dc3980ca7
--- /dev/null
+++ b/modules/createAll.js
@@ -0,0 +1,54 @@
+import createRouter from './Router'
+import createLink from './Link'
+import createIndexLink from './IndexLink'
+import createIndexRoute from './IndexRoute'
+import createRedirect from './Redirect';
+import createRoute from './Route'
+import createHistory from './History'
+import createLifecycle from './Lifecycle'
+import createRouteContext from './RouteContext'
+import createUseRoutes from './useRoutes'
+import createRouteUtils from './RouteUtils'
+import createRoutingContext from './RoutingContext'
+import createPropTypes from './PropTypes'
+import createMatch from './match'
+
+
+export default function createAll(React) {
+ const Router = createRouter(React)
+ const Link = createLink(React)
+ const IndexLink = createIndexLink(React)
+ const IndexRoute = createIndexRoute(React)
+ const Redirect = createRedirect(React)
+ const Route = createRoute(React)
+ const History = createHistory(React)
+ const Lifecycle = createLifecycle(React)
+ const RouteContext = createRouteContext(React)
+ const useRoutes = createUseRoutes(React)
+ const { createRoutes } = createRouteUtils(React)
+ const RoutingContext = createRoutingContext(React)
+ const PropTypes = createPropTypes(React)
+ const match = createMatch(React)
+
+ return {
+ /* components */
+ Router,
+ Link,
+ IndexLink,
+ /* components (configuration) */
+ IndexRoute,
+ Redirect,
+ Route,
+ /* mixins */
+ History,
+ Lifecycle,
+ RouteContext,
+
+ /* utils */
+ useRoutes,
+ createRoutes,
+ RoutingContext,
+ PropTypes,
+ match
+ }
+}
diff --git a/modules/index.js b/modules/index.js
index a8d7adf8e4..fa382559d6 100644
--- a/modules/index.js
+++ b/modules/index.js
@@ -1,23 +1,23 @@
-/* components */
-export Router from './Router'
-export Link from './Link'
-export IndexLink from './IndexLink'
+import React from 'react'
+import createAll from './createAll'
-/* components (configuration) */
-export IndexRoute from './IndexRoute'
-export Redirect from './Redirect'
-export Route from './Route'
+const all = createAll(React)
-/* mixins */
-export History from './History'
-export Lifecycle from './Lifecycle'
-export RouteContext from './RouteContext'
+export const {
+ Router,
+ Link,
+ IndexLink,
+ IndexRoute,
+ Redirect,
+ Route,
+ History,
+ Lifecycle,
+ RouteContext,
+ useRoutes,
+ createRoutes,
+ RoutingContext,
+ PropTypes,
+ match
+} = all
-/* utils */
-export useRoutes from './useRoutes'
-export { createRoutes } from './RouteUtils'
-export RoutingContext from './RoutingContext'
-export PropTypes from './PropTypes'
-export match from './match'
-
-export default from './Router'
+export default all.Router
diff --git a/modules/match.js b/modules/match.js
index a6094c6aa5..fe98200df3 100644
--- a/modules/match.js
+++ b/modules/match.js
@@ -1,25 +1,32 @@
import createMemoryHistory from 'history/lib/createMemoryHistory'
-import useRoutes from './useRoutes'
-import { createRoutes } from './RouteUtils'
+import createUseRoutes from './useRoutes'
+import createRouteUtils from './RouteUtils'
-export default function match({
- routes,
- history,
- location,
- parseQueryString,
- stringifyQuery
-}, cb) {
- let createHistory = history ? () => history : createMemoryHistory
+export default function createMatch(React) {
- let staticHistory = useRoutes(createHistory)({
- routes: createRoutes(routes),
+ const useRoutes = createUseRoutes(React);
+ const { createRoutes } = createRouteUtils(React)
+
+ function match({
+ routes,
+ history,
+ location,
parseQueryString,
stringifyQuery
- })
+ }, cb) {
+ let createHistory = history ? () => history : createMemoryHistory
- staticHistory.match(location, function (error, nextLocation, nextState) {
- let renderProps = nextState ? {...nextState, history: staticHistory} : null
- cb(error, nextLocation, renderProps)
- })
-}
+ let staticHistory = useRoutes(createHistory)({
+ routes: createRoutes(routes),
+ parseQueryString,
+ stringifyQuery
+ })
+ staticHistory.match(location, function (error, nextLocation, nextState) {
+ let renderProps = nextState ? {...nextState, history: staticHistory} : null
+ cb(error, nextLocation, renderProps)
+ })
+ }
+
+ return match
+}
diff --git a/modules/matchRoutes.js b/modules/matchRoutes.js
index a1c28c8211..e5e4e612c5 100644
--- a/modules/matchRoutes.js
+++ b/modules/matchRoutes.js
@@ -1,126 +1,132 @@
import { loopAsync } from './AsyncUtils'
import { matchPattern } from './PatternUtils'
-import { createRoutes } from './RouteUtils'
-
-function getChildRoutes(route, location, callback) {
- if (route.childRoutes) {
- callback(null, route.childRoutes)
- } else if (route.getChildRoutes) {
- route.getChildRoutes(location, function(error, childRoutes) {
- callback(error, !error && createRoutes(childRoutes))
- })
- } else {
- callback()
- }
-}
+import createRouteUtils from './RouteUtils'
-function getIndexRoute(route, location, callback) {
- if (route.indexRoute) {
- callback(null, route.indexRoute)
- } else if (route.getIndexRoute) {
- route.getIndexRoute(location, function(error, indexRoute) {
- callback(error, !error && createRoutes(indexRoute)[0])
- })
- } else {
- callback()
- }
-}
+export default function createMatchRoutes(React) {
-function assignParams(params, paramNames, paramValues) {
- return paramNames.reduceRight(function (params, paramName, index) {
- const paramValue = paramValues && paramValues[index]
+ const { createRoutes } = createRouteUtils(React)
- if (Array.isArray(params[paramName])) {
- params[paramName].unshift(paramValue)
- } else if (paramName in params) {
- params[paramName] = [ paramValue, params[paramName] ]
+ function getChildRoutes(route, location, callback) {
+ if (route.childRoutes) {
+ callback(null, route.childRoutes)
+ } else if (route.getChildRoutes) {
+ route.getChildRoutes(location, function(error, childRoutes) {
+ callback(error, !error && createRoutes(childRoutes))
+ })
} else {
- params[paramName] = paramValue
+ callback()
}
+ }
- return params
- }, params)
-}
+ function getIndexRoute(route, location, callback) {
+ if (route.indexRoute) {
+ callback(null, route.indexRoute)
+ } else if (route.getIndexRoute) {
+ route.getIndexRoute(location, function(error, indexRoute) {
+ callback(error, !error && createRoutes(indexRoute)[0])
+ })
+ } else {
+ callback()
+ }
+ }
-function createParams(paramNames, paramValues) {
- return assignParams({}, paramNames, paramValues)
-}
+ function assignParams(params, paramNames, paramValues) {
+ return paramNames.reduceRight(function (params, paramName, index) {
+ const paramValue = paramValues && paramValues[index]
-function matchRouteDeep(basename, route, location, callback) {
- let pattern = route.path || ''
+ if (Array.isArray(params[paramName])) {
+ params[paramName].unshift(paramValue)
+ } else if (paramName in params) {
+ params[paramName] = [ paramValue, params[paramName] ]
+ } else {
+ params[paramName] = paramValue
+ }
- if (pattern.indexOf('/') !== 0)
- pattern = basename.replace(/\/*$/, '/') + pattern // Relative paths build on the parent's path.
+ return params
+ }, params)
+ }
- const { remainingPathname, paramNames, paramValues } = matchPattern(pattern, location.pathname)
- const isExactMatch = remainingPathname === ''
+ function createParams(paramNames, paramValues) {
+ return assignParams({}, paramNames, paramValues)
+ }
- if (isExactMatch && route.path) {
- const match = {
- routes: [ route ],
- params: createParams(paramNames, paramValues)
- }
+ function matchRouteDeep(basename, route, location, callback) {
+ let pattern = route.path || ''
- getIndexRoute(route, location, function (error, indexRoute) {
- if (error) {
- callback(error)
- } else {
- if (indexRoute)
- match.routes.push(indexRoute)
+ if (pattern.indexOf('/') !== 0)
+ pattern = basename.replace(/\/*$/, '/') + pattern // Relative paths build on the parent's path.
- callback(null, match)
- }
- })
- } else if (remainingPathname != null || route.childRoutes) {
- // Either a) this route matched at least some of the path or b)
- // we don't have to load this route's children asynchronously. In
- // either case continue checking for matches in the subtree.
- getChildRoutes(route, location, function (error, childRoutes) {
- if (error) {
- callback(error)
- } else if (childRoutes) {
- // Check the child routes to see if any of them match.
- matchRoutes(childRoutes, location, function (error, match) {
- if (error) {
- callback(error)
- } else if (match) {
- // A child route matched! Augment the match and pass it up the stack.
- match.routes.unshift(route)
- callback(null, match)
- } else {
- callback()
- }
- }, pattern)
- } else {
- callback()
+ const { remainingPathname, paramNames, paramValues } = matchPattern(pattern, location.pathname)
+ const isExactMatch = remainingPathname === ''
+
+ if (isExactMatch && route.path) {
+ const match = {
+ routes: [ route ],
+ params: createParams(paramNames, paramValues)
}
- })
- } else {
- callback()
+
+ getIndexRoute(route, location, function (error, indexRoute) {
+ if (error) {
+ callback(error)
+ } else {
+ if (indexRoute)
+ match.routes.push(indexRoute)
+
+ callback(null, match)
+ }
+ })
+ } else if (remainingPathname != null || route.childRoutes) {
+ // Either a) this route matched at least some of the path or b)
+ // we don't have to load this route's children asynchronously. In
+ // either case continue checking for matches in the subtree.
+ getChildRoutes(route, location, function (error, childRoutes) {
+ if (error) {
+ callback(error)
+ } else if (childRoutes) {
+ // Check the child routes to see if any of them match.
+ matchRoutes(childRoutes, location, function (error, match) {
+ if (error) {
+ callback(error)
+ } else if (match) {
+ // A child route matched! Augment the match and pass it up the stack.
+ match.routes.unshift(route)
+ callback(null, match)
+ } else {
+ callback()
+ }
+ }, pattern)
+ } else {
+ callback()
+ }
+ })
+ } else {
+ callback()
+ }
}
-}
-/**
- * Asynchronously matches the given location to a set of routes and calls
- * callback(error, state) when finished. The state object will have the
- * following properties:
- *
- * - routes An array of routes that matched, in hierarchical order
- * - params An object of URL parameters
- *
- * Note: This operation may finish synchronously if no routes have an
- * asynchronous getChildRoutes method.
- */
-function matchRoutes(routes, location, callback, basename='') {
- loopAsync(routes.length, function (index, next, done) {
- matchRouteDeep(basename, routes[index], location, 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 will have the
+ * following properties:
+ *
+ * - routes An array of routes that matched, in hierarchical order
+ * - params An object of URL parameters
+ *
+ * Note: This operation may finish synchronously if no routes have an
+ * asynchronous getChildRoutes method.
+ */
+ function matchRoutes(routes, location, callback, basename='') {
+ loopAsync(routes.length, function (index, next, done) {
+ matchRouteDeep(basename, routes[index], location, function (error, match) {
+ if (error || match) {
+ done(error, match)
+ } else {
+ next()
+ }
+ })
+ }, callback)
+ }
+
+ return matchRoutes
-export default matchRoutes
+}
diff --git a/modules/native.js b/modules/native.js
new file mode 100644
index 0000000000..f1d956271f
--- /dev/null
+++ b/modules/native.js
@@ -0,0 +1,23 @@
+import React from 'react-native'
+import createAll from './createAll'
+
+const all = createAll(React)
+
+export const {
+ Router,
+ Link,
+ IndexLink,
+ IndexRoute,
+ Redirect,
+ Route,
+ History,
+ Lifecycle,
+ RouteContext,
+ useRoutes,
+ createRoutes,
+ RoutingContext,
+ PropTypes,
+ match
+} = all
+
+export default all.Router
diff --git a/modules/useRoutes.js b/modules/useRoutes.js
index 8fd9ca26d4..8dcec5404b 100644
--- a/modules/useRoutes.js
+++ b/modules/useRoutes.js
@@ -6,235 +6,241 @@ import computeChangedRoutes from './computeChangedRoutes'
import { runEnterHooks, runLeaveHooks } from './TransitionUtils'
import { default as _isActive } from './isActive'
import getComponents from './getComponents'
-import matchRoutes from './matchRoutes'
+import createMatchRoutes from './matchRoutes'
-function hasAnyProperties(object) {
- for (const p in object)
- if (object.hasOwnProperty(p))
- return true
+export default function createUseRoutes(React) {
- return false
-}
+ const matchRoutes = createMatchRoutes(React)
-/**
- * Returns a new createHistory function that may be used to create
- * history objects that know about routing.
- *
- * - isActive(pathname, query)
- * - registerRouteHook(route, (location) => {})
- * - unregisterRouteHook(route, (location) => {})
- * - match(location, (error, nextState, nextLocation) => {})
- * - listen((error, nextState) => {})
- */
-function useRoutes(createHistory) {
- return function (options={}) {
- let { routes, ...historyOptions } = options
- let history = useQueries(createHistory)(historyOptions)
- let state = {}
-
- function isActive(pathname, query, indexOnly=false) {
- return _isActive(pathname, query, indexOnly, state.location, state.routes, state.params)
- }
+ function hasAnyProperties(object) {
+ for (const p in object)
+ if (object.hasOwnProperty(p))
+ return true
- let partialNextState
+ return false
+ }
- function match(location, callback) {
- if (partialNextState && partialNextState.location === location) {
- // Continue from where we left off.
- finishMatch(partialNextState, callback)
- } else {
- matchRoutes(routes, location, function (error, nextState) {
- if (error) {
- callback(error, null, null)
- } else if (nextState) {
- finishMatch({ ...nextState, location }, function (err, nextLocation, nextState) {
- if (nextState)
- state = nextState
- callback(err, nextLocation, nextState)
- })
- } else {
- callback(null, null, null)
- }
- })
+ /**
+ * Returns a new createHistory function that may be used to create
+ * history objects that know about routing.
+ *
+ * - isActive(pathname, query)
+ * - registerRouteHook(route, (location) => {})
+ * - unregisterRouteHook(route, (location) => {})
+ * - match(location, (error, nextState, nextLocation) => {})
+ * - listen((error, nextState) => {})
+ */
+ function useRoutes(createHistory) {
+ return function (options={}) {
+ let { routes, ...historyOptions } = options
+ let history = useQueries(createHistory)(historyOptions)
+ let state = {}
+
+ function isActive(pathname, query, indexOnly=false) {
+ return _isActive(pathname, query, indexOnly, state.location, state.routes, state.params)
}
- }
-
- function createLocationFromRedirectInfo({ pathname, query, state }) {
- return createLocation(
- history.createPath(pathname, query), state, REPLACE, history.createKey()
- )
- }
-
- function finishMatch(nextState, callback) {
- let { leaveRoutes, enterRoutes } = computeChangedRoutes(state, nextState)
- runLeaveHooks(leaveRoutes)
+ let partialNextState
- runEnterHooks(enterRoutes, nextState, function (error, redirectInfo) {
- if (error) {
- callback(error)
- } else if (redirectInfo) {
- callback(null, createLocationFromRedirectInfo(redirectInfo), null)
+ function match(location, callback) {
+ if (partialNextState && partialNextState.location === location) {
+ // Continue from where we left off.
+ finishMatch(partialNextState, callback)
} else {
- // TODO: Fetch components after state is updated.
- getComponents(nextState, function (error, components) {
+ matchRoutes(routes, location, function (error, nextState) {
if (error) {
- callback(error)
+ callback(error, null, null)
+ } else if (nextState) {
+ finishMatch({ ...nextState, location }, function (err, nextLocation, nextState) {
+ if (nextState)
+ state = nextState
+ callback(err, nextLocation, nextState)
+ })
} else {
- callback(null, null, { ...nextState, components })
+ callback(null, null, null)
}
})
}
- })
- }
-
- const RouteHooks = {}
+ }
- let RouteGuid = 1
+ function createLocationFromRedirectInfo({ pathname, query, state }) {
+ return createLocation(
+ history.createPath(pathname, query), state, REPLACE, history.createKey()
+ )
+ }
- function getRouteID(route) {
- return route.__id__ || (route.__id__ = RouteGuid++)
- }
+ function finishMatch(nextState, callback) {
+ let { leaveRoutes, enterRoutes } = computeChangedRoutes(state, nextState)
- function getRouteHooksForRoutes(routes) {
- return routes.reduce(function (hooks, route) {
- hooks.push.apply(hooks, RouteHooks[getRouteID(route)])
- return hooks
- }, [])
- }
+ runLeaveHooks(leaveRoutes)
- function transitionHook(location, callback) {
- matchRoutes(routes, location, function (error, nextState) {
- if (nextState == null) {
- // TODO: We didn't actually match anything, but hang
- // onto error/nextState so we don't have to matchRoutes
- // again in the listen callback.
- callback()
- return
- }
+ runEnterHooks(enterRoutes, nextState, function (error, redirectInfo) {
+ if (error) {
+ callback(error)
+ } else if (redirectInfo) {
+ callback(null, createLocationFromRedirectInfo(redirectInfo), null)
+ } else {
+ // TODO: Fetch components after state is updated.
+ getComponents(nextState, function (error, components) {
+ if (error) {
+ callback(error)
+ } else {
+ callback(null, null, { ...nextState, components })
+ }
+ })
+ }
+ })
+ }
- // Cache some state here so we don't have to
- // matchRoutes() again in the listen callback.
- partialNextState = { ...nextState, location }
+ const RouteHooks = {}
- let hooks = getRouteHooksForRoutes(
- computeChangedRoutes(state, nextState).leaveRoutes
- )
+ let RouteGuid = 1
- let result
- for (let i = 0, len = hooks.length; result == null && i < len; ++i) {
- // Passing the location arg here indicates to
- // the user that this is a transition hook.
- result = hooks[i](location)
- }
+ function getRouteID(route) {
+ return route.__id__ || (route.__id__ = RouteGuid++)
+ }
- callback(result)
- })
- }
+ function getRouteHooksForRoutes(routes) {
+ return routes.reduce(function (hooks, route) {
+ hooks.push.apply(hooks, RouteHooks[getRouteID(route)])
+ return hooks
+ }, [])
+ }
- function beforeUnloadHook() {
- // Synchronously check to see if any route hooks want to
- // prevent the current window/tab from closing.
- if (state.routes) {
- let hooks = getRouteHooksForRoutes(state.routes)
-
- let message
- for (let i = 0, len = hooks.length; typeof message !== 'string' && i < len; ++i) {
- // Passing no args indicates to the user that this is a
- // beforeunload hook. We don't know the next location.
- message = hooks[i]()
- }
+ function transitionHook(location, callback) {
+ matchRoutes(routes, location, function (error, nextState) {
+ if (nextState == null) {
+ // TODO: We didn't actually match anything, but hang
+ // onto error/nextState so we don't have to matchRoutes
+ // again in the listen callback.
+ callback()
+ return
+ }
- return message
- }
- }
+ // Cache some state here so we don't have to
+ // matchRoutes() again in the listen callback.
+ partialNextState = { ...nextState, location }
- function registerRouteHook(route, hook) {
- // TODO: Warn if they register for a route that isn't currently
- // active. They're probably doing something wrong, like re-creating
- // route objects on every location change.
- let routeID = getRouteID(route)
- let hooks = RouteHooks[routeID]
+ let hooks = getRouteHooksForRoutes(
+ computeChangedRoutes(state, nextState).leaveRoutes
+ )
- if (hooks == null) {
- let thereWereNoRouteHooks = !hasAnyProperties(RouteHooks)
+ let result
+ for (let i = 0, len = hooks.length; result == null && i < len; ++i) {
+ // Passing the location arg here indicates to
+ // the user that this is a transition hook.
+ result = hooks[i](location)
+ }
- hooks = RouteHooks[routeID] = [ hook ]
+ callback(result)
+ })
+ }
- if (thereWereNoRouteHooks) {
- history.registerTransitionHook(transitionHook)
+ function beforeUnloadHook() {
+ // Synchronously check to see if any route hooks want to
+ // prevent the current window/tab from closing.
+ if (state.routes) {
+ let hooks = getRouteHooksForRoutes(state.routes)
+
+ let message
+ for (let i = 0, len = hooks.length; typeof message !== 'string' && i < len; ++i) {
+ // Passing no args indicates to the user that this is a
+ // beforeunload hook. We don't know the next location.
+ message = hooks[i]()
+ }
- if (history.registerBeforeUnloadHook)
- history.registerBeforeUnloadHook(beforeUnloadHook)
+ return message
}
- } else if (hooks.indexOf(hook) === -1) {
- hooks.push(hook)
}
- }
- function unregisterRouteHook(route, hook) {
- let routeID = getRouteID(route)
- let hooks = RouteHooks[routeID]
+ function registerRouteHook(route, hook) {
+ // TODO: Warn if they register for a route that isn't currently
+ // active. They're probably doing something wrong, like re-creating
+ // route objects on every location change.
+ let routeID = getRouteID(route)
+ let hooks = RouteHooks[routeID]
- if (hooks != null) {
- let newHooks = hooks.filter(item => item !== hook)
+ if (hooks == null) {
+ let thereWereNoRouteHooks = !hasAnyProperties(RouteHooks)
- if (newHooks.length === 0) {
- delete RouteHooks[routeID]
+ hooks = RouteHooks[routeID] = [ hook ]
- if (!hasAnyProperties(RouteHooks)) {
- history.unregisterTransitionHook(transitionHook)
+ if (thereWereNoRouteHooks) {
+ history.registerTransitionHook(transitionHook)
- if (history.unregisterBeforeUnloadHook)
- history.unregisterBeforeUnloadHook(beforeUnloadHook)
+ if (history.registerBeforeUnloadHook)
+ history.registerBeforeUnloadHook(beforeUnloadHook)
}
- } else {
- RouteHooks[routeID] = newHooks
+ } else if (hooks.indexOf(hook) === -1) {
+ hooks.push(hook)
}
}
- }
- /**
- * This is the API for stateful environments. As the location changes,
- * we update state and call the listener. Benefits of this API are:
- *
- * - We automatically manage state on the client
- * - We automatically handle redirects on the client
- * - We warn when the location doesn't match any routes
- */
- function listen(listener) {
- return history.listen(function (location) {
- if (state.location === location) {
- listener(null, state)
- } else {
- match(location, function (error, nextLocation, nextState) {
- if (error) {
- listener(error)
- } else if (nextState) {
- listener(null, state) // match mutates state to nextState
- } else if (nextLocation) {
- history.transitionTo(nextLocation)
- } else {
- warning(
- false,
- 'Location "%s" did not match any routes',
- location.pathname + location.search
- )
+ function unregisterRouteHook(route, hook) {
+ let routeID = getRouteID(route)
+ let hooks = RouteHooks[routeID]
+
+ if (hooks != null) {
+ let newHooks = hooks.filter(item => item !== hook)
+
+ if (newHooks.length === 0) {
+ delete RouteHooks[routeID]
+
+ if (!hasAnyProperties(RouteHooks)) {
+ history.unregisterTransitionHook(transitionHook)
+
+ if (history.unregisterBeforeUnloadHook)
+ history.unregisterBeforeUnloadHook(beforeUnloadHook)
}
- })
+ } else {
+ RouteHooks[routeID] = newHooks
+ }
}
- })
- }
+ }
- return {
- ...history,
- isActive,
- registerRouteHook,
- unregisterRouteHook,
- listen,
- match
+ /**
+ * This is the API for stateful environments. As the location changes,
+ * we update state and call the listener. Benefits of this API are:
+ *
+ * - We automatically manage state on the client
+ * - We automatically handle redirects on the client
+ * - We warn when the location doesn't match any routes
+ */
+ function listen(listener) {
+ return history.listen(function (location) {
+ if (state.location === location) {
+ listener(null, state)
+ } else {
+ match(location, function (error, nextLocation, nextState) {
+ if (error) {
+ listener(error)
+ } else if (nextState) {
+ listener(null, state) // match mutates state to nextState
+ } else if (nextLocation) {
+ history.transitionTo(nextLocation)
+ } else {
+ warning(
+ false,
+ 'Location "%s" did not match any routes',
+ location.pathname + location.search
+ )
+ }
+ })
+ }
+ })
+ }
+
+ return {
+ ...history,
+ isActive,
+ registerRouteHook,
+ unregisterRouteHook,
+ listen,
+ match
+ }
}
}
-}
-export default useRoutes
+ return useRoutes
+
+}
diff --git a/native.js b/native.js
new file mode 100644
index 0000000000..0c3ed853ba
--- /dev/null
+++ b/native.js
@@ -0,0 +1 @@
+module.exports = require('./lib/native');