UNPKG

23.1 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
6
7var createContext = _interopDefault(require('create-react-context'));
8var React = _interopDefault(require('react'));
9var PropTypes = _interopDefault(require('prop-types'));
10var warning = _interopDefault(require('tiny-warning'));
11var history = require('history');
12var invariant = _interopDefault(require('tiny-invariant'));
13var pathToRegexp = _interopDefault(require('path-to-regexp'));
14var reactIs = require('react-is');
15var hoistStatics = _interopDefault(require('hoist-non-react-statics'));
16
17function _extends() {
18 _extends = Object.assign || function (target) {
19 for (var i = 1; i < arguments.length; i++) {
20 var source = arguments[i];
21
22 for (var key in source) {
23 if (Object.prototype.hasOwnProperty.call(source, key)) {
24 target[key] = source[key];
25 }
26 }
27 }
28
29 return target;
30 };
31
32 return _extends.apply(this, arguments);
33}
34
35function _inheritsLoose(subClass, superClass) {
36 subClass.prototype = Object.create(superClass.prototype);
37 subClass.prototype.constructor = subClass;
38 subClass.__proto__ = superClass;
39}
40
41function _objectWithoutPropertiesLoose(source, excluded) {
42 if (source == null) return {};
43 var target = {};
44 var sourceKeys = Object.keys(source);
45 var key, i;
46
47 for (i = 0; i < sourceKeys.length; i++) {
48 key = sourceKeys[i];
49 if (excluded.indexOf(key) >= 0) continue;
50 target[key] = source[key];
51 }
52
53 return target;
54}
55
56// TODO: Replace with React.createContext once we can assume React 16+
57
58var createNamedContext = function createNamedContext(name) {
59 var context = createContext();
60 context.Provider.displayName = name + ".Provider";
61 context.Consumer.displayName = name + ".Consumer";
62 return context;
63};
64
65var context =
66/*#__PURE__*/
67createNamedContext('Router');
68
69/**
70 * The public API for putting history on context.
71 */
72
73var Router =
74/*#__PURE__*/
75function (_React$Component) {
76 _inheritsLoose(Router, _React$Component);
77
78 Router.computeRootMatch = function computeRootMatch(pathname) {
79 return {
80 path: "/",
81 url: "/",
82 params: {},
83 isExact: pathname === "/"
84 };
85 };
86
87 function Router(props) {
88 var _this;
89
90 _this = _React$Component.call(this, props) || this;
91 _this.state = {
92 location: props.history.location
93 }; // This is a bit of a hack. We have to start listening for location
94 // changes here in the constructor in case there are any <Redirect>s
95 // on the initial render. If there are, they will replace/push when
96 // they mount and since cDM fires in children before parents, we may
97 // get a new location before the <Router> is mounted.
98
99 _this._isMounted = false;
100 _this._pendingLocation = null;
101
102 if (!props.staticContext) {
103 _this.unlisten = props.history.listen(function (location) {
104 if (_this._isMounted) {
105 _this.setState({
106 location: location
107 });
108 } else {
109 _this._pendingLocation = location;
110 }
111 });
112 }
113
114 return _this;
115 }
116
117 var _proto = Router.prototype;
118
119 _proto.componentDidMount = function componentDidMount() {
120 this._isMounted = true;
121
122 if (this._pendingLocation) {
123 this.setState({
124 location: this._pendingLocation
125 });
126 }
127 };
128
129 _proto.componentWillUnmount = function componentWillUnmount() {
130 if (this.unlisten) this.unlisten();
131 };
132
133 _proto.render = function render() {
134 return React.createElement(context.Provider, {
135 children: this.props.children || null,
136 value: {
137 history: this.props.history,
138 location: this.state.location,
139 match: Router.computeRootMatch(this.state.location.pathname),
140 staticContext: this.props.staticContext
141 }
142 });
143 };
144
145 return Router;
146}(React.Component);
147
148{
149 Router.propTypes = {
150 children: PropTypes.node,
151 history: PropTypes.object.isRequired,
152 staticContext: PropTypes.object
153 };
154
155 Router.prototype.componentDidUpdate = function (prevProps) {
156 warning(prevProps.history === this.props.history, "You cannot change <Router history>");
157 };
158}
159
160/**
161 * The public API for a <Router> that stores location in memory.
162 */
163
164var MemoryRouter =
165/*#__PURE__*/
166function (_React$Component) {
167 _inheritsLoose(MemoryRouter, _React$Component);
168
169 function MemoryRouter() {
170 var _this;
171
172 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
173 args[_key] = arguments[_key];
174 }
175
176 _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
177 _this.history = history.createMemoryHistory(_this.props);
178 return _this;
179 }
180
181 var _proto = MemoryRouter.prototype;
182
183 _proto.render = function render() {
184 return React.createElement(Router, {
185 history: this.history,
186 children: this.props.children
187 });
188 };
189
190 return MemoryRouter;
191}(React.Component);
192
193{
194 MemoryRouter.propTypes = {
195 initialEntries: PropTypes.array,
196 initialIndex: PropTypes.number,
197 getUserConfirmation: PropTypes.func,
198 keyLength: PropTypes.number,
199 children: PropTypes.node
200 };
201
202 MemoryRouter.prototype.componentDidMount = function () {
203 warning(!this.props.history, "<MemoryRouter> ignores the history prop. To use a custom history, " + "use `import { Router }` instead of `import { MemoryRouter as Router }`.");
204 };
205}
206
207var Lifecycle =
208/*#__PURE__*/
209function (_React$Component) {
210 _inheritsLoose(Lifecycle, _React$Component);
211
212 function Lifecycle() {
213 return _React$Component.apply(this, arguments) || this;
214 }
215
216 var _proto = Lifecycle.prototype;
217
218 _proto.componentDidMount = function componentDidMount() {
219 if (this.props.onMount) this.props.onMount.call(this, this);
220 };
221
222 _proto.componentDidUpdate = function componentDidUpdate(prevProps) {
223 if (this.props.onUpdate) this.props.onUpdate.call(this, this, prevProps);
224 };
225
226 _proto.componentWillUnmount = function componentWillUnmount() {
227 if (this.props.onUnmount) this.props.onUnmount.call(this, this);
228 };
229
230 _proto.render = function render() {
231 return null;
232 };
233
234 return Lifecycle;
235}(React.Component);
236
237/**
238 * The public API for prompting the user before navigating away from a screen.
239 */
240
241function Prompt(_ref) {
242 var message = _ref.message,
243 _ref$when = _ref.when,
244 when = _ref$when === void 0 ? true : _ref$when;
245 return React.createElement(context.Consumer, null, function (context$$1) {
246 !context$$1 ? invariant(false, "You should not use <Prompt> outside a <Router>") : void 0;
247 if (!when || context$$1.staticContext) return null;
248 var method = context$$1.history.block;
249 return React.createElement(Lifecycle, {
250 onMount: function onMount(self) {
251 self.release = method(message);
252 },
253 onUpdate: function onUpdate(self, prevProps) {
254 if (prevProps.message !== message) {
255 self.release();
256 self.release = method(message);
257 }
258 },
259 onUnmount: function onUnmount(self) {
260 self.release();
261 },
262 message: message
263 });
264 });
265}
266
267{
268 var messageType = PropTypes.oneOfType([PropTypes.func, PropTypes.string]);
269 Prompt.propTypes = {
270 when: PropTypes.bool,
271 message: messageType.isRequired
272 };
273}
274
275var cache = {};
276var cacheLimit = 10000;
277var cacheCount = 0;
278
279function compilePath(path) {
280 if (cache[path]) return cache[path];
281 var generator = pathToRegexp.compile(path);
282
283 if (cacheCount < cacheLimit) {
284 cache[path] = generator;
285 cacheCount++;
286 }
287
288 return generator;
289}
290/**
291 * Public API for generating a URL pathname from a path and parameters.
292 */
293
294
295function generatePath(path, params) {
296 if (path === void 0) {
297 path = "/";
298 }
299
300 if (params === void 0) {
301 params = {};
302 }
303
304 return path === "/" ? path : compilePath(path)(params, {
305 pretty: true
306 });
307}
308
309/**
310 * The public API for navigating programmatically with a component.
311 */
312
313function Redirect(_ref) {
314 var computedMatch = _ref.computedMatch,
315 to = _ref.to,
316 _ref$push = _ref.push,
317 push = _ref$push === void 0 ? false : _ref$push;
318 return React.createElement(context.Consumer, null, function (context$$1) {
319 !context$$1 ? invariant(false, "You should not use <Redirect> outside a <Router>") : void 0;
320 var history$$1 = context$$1.history,
321 staticContext = context$$1.staticContext;
322 var method = push ? history$$1.push : history$$1.replace;
323 var location = history.createLocation(computedMatch ? typeof to === "string" ? generatePath(to, computedMatch.params) : _extends({}, to, {
324 pathname: generatePath(to.pathname, computedMatch.params)
325 }) : to); // When rendering in a static context,
326 // set the new location immediately.
327
328 if (staticContext) {
329 method(location);
330 return null;
331 }
332
333 return React.createElement(Lifecycle, {
334 onMount: function onMount() {
335 method(location);
336 },
337 onUpdate: function onUpdate(self, prevProps) {
338 if (!history.locationsAreEqual(prevProps.to, location)) {
339 method(location);
340 }
341 },
342 to: to
343 });
344 });
345}
346
347{
348 Redirect.propTypes = {
349 push: PropTypes.bool,
350 from: PropTypes.string,
351 to: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired
352 };
353}
354
355var cache$1 = {};
356var cacheLimit$1 = 10000;
357var cacheCount$1 = 0;
358
359function compilePath$1(path, options) {
360 var cacheKey = "" + options.end + options.strict + options.sensitive;
361 var pathCache = cache$1[cacheKey] || (cache$1[cacheKey] = {});
362 if (pathCache[path]) return pathCache[path];
363 var keys = [];
364 var regexp = pathToRegexp(path, keys, options);
365 var result = {
366 regexp: regexp,
367 keys: keys
368 };
369
370 if (cacheCount$1 < cacheLimit$1) {
371 pathCache[path] = result;
372 cacheCount$1++;
373 }
374
375 return result;
376}
377/**
378 * Public API for matching a URL pathname to a path.
379 */
380
381
382function matchPath(pathname, options) {
383 if (options === void 0) {
384 options = {};
385 }
386
387 if (typeof options === "string") options = {
388 path: options
389 };
390 var _options = options,
391 path = _options.path,
392 _options$exact = _options.exact,
393 exact = _options$exact === void 0 ? false : _options$exact,
394 _options$strict = _options.strict,
395 strict = _options$strict === void 0 ? false : _options$strict,
396 _options$sensitive = _options.sensitive,
397 sensitive = _options$sensitive === void 0 ? false : _options$sensitive;
398 var paths = [].concat(path);
399 return paths.reduce(function (matched, path) {
400 if (matched) return matched;
401
402 var _compilePath = compilePath$1(path, {
403 end: exact,
404 strict: strict,
405 sensitive: sensitive
406 }),
407 regexp = _compilePath.regexp,
408 keys = _compilePath.keys;
409
410 var match = regexp.exec(pathname);
411 if (!match) return null;
412 var url = match[0],
413 values = match.slice(1);
414 var isExact = pathname === url;
415 if (exact && !isExact) return null;
416 return {
417 path: path,
418 // the path used to match
419 url: path === "/" && url === "" ? "/" : url,
420 // the matched portion of the URL
421 isExact: isExact,
422 // whether or not we matched exactly
423 params: keys.reduce(function (memo, key, index) {
424 memo[key.name] = values[index];
425 return memo;
426 }, {})
427 };
428 }, null);
429}
430
431function isEmptyChildren(children) {
432 return React.Children.count(children) === 0;
433}
434/**
435 * The public API for matching a single path and rendering.
436 */
437
438
439var Route =
440/*#__PURE__*/
441function (_React$Component) {
442 _inheritsLoose(Route, _React$Component);
443
444 function Route() {
445 return _React$Component.apply(this, arguments) || this;
446 }
447
448 var _proto = Route.prototype;
449
450 _proto.render = function render() {
451 var _this = this;
452
453 return React.createElement(context.Consumer, null, function (context$$1) {
454 !context$$1 ? invariant(false, "You should not use <Route> outside a <Router>") : void 0;
455 var location = _this.props.location || context$$1.location;
456 var match = _this.props.computedMatch ? _this.props.computedMatch // <Switch> already computed the match for us
457 : _this.props.path ? matchPath(location.pathname, _this.props) : context$$1.match;
458
459 var props = _extends({}, context$$1, {
460 location: location,
461 match: match
462 });
463
464 var _this$props = _this.props,
465 children = _this$props.children,
466 component = _this$props.component,
467 render = _this$props.render; // Preact uses an empty array as children by
468 // default, so use null if that's the case.
469
470 if (Array.isArray(children) && children.length === 0) {
471 children = null;
472 }
473
474 if (typeof children === "function") {
475 children = children(props);
476
477 if (children === undefined) {
478 {
479 var path = _this.props.path;
480 warning(false, "You returned `undefined` from the `children` function of " + ("<Route" + (path ? " path=\"" + path + "\"" : "") + ">, but you ") + "should have returned a React element or `null`");
481 }
482
483 children = null;
484 }
485 }
486
487 return React.createElement(context.Provider, {
488 value: props
489 }, children && !isEmptyChildren(children) ? children : props.match ? component ? React.createElement(component, props) : render ? render(props) : null : null);
490 });
491 };
492
493 return Route;
494}(React.Component);
495
496{
497 Route.propTypes = {
498 children: PropTypes.oneOfType([PropTypes.func, PropTypes.node]),
499 component: function component(props, propName) {
500 if (props[propName] && !reactIs.isValidElementType(props[propName])) {
501 return new Error("Invalid prop 'component' supplied to 'Route': the prop is not a valid React component");
502 }
503 },
504 exact: PropTypes.bool,
505 location: PropTypes.object,
506 path: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
507 render: PropTypes.func,
508 sensitive: PropTypes.bool,
509 strict: PropTypes.bool
510 };
511
512 Route.prototype.componentDidMount = function () {
513 warning(!(this.props.children && !isEmptyChildren(this.props.children) && this.props.component), "You should not use <Route component> and <Route children> in the same route; <Route component> will be ignored");
514 warning(!(this.props.children && !isEmptyChildren(this.props.children) && this.props.render), "You should not use <Route render> and <Route children> in the same route; <Route render> will be ignored");
515 warning(!(this.props.component && this.props.render), "You should not use <Route component> and <Route render> in the same route; <Route render> will be ignored");
516 };
517
518 Route.prototype.componentDidUpdate = function (prevProps) {
519 warning(!(this.props.location && !prevProps.location), '<Route> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.');
520 warning(!(!this.props.location && prevProps.location), '<Route> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.');
521 };
522}
523
524function addLeadingSlash(path) {
525 return path.charAt(0) === "/" ? path : "/" + path;
526}
527
528function addBasename(basename, location) {
529 if (!basename) return location;
530 return _extends({}, location, {
531 pathname: addLeadingSlash(basename) + location.pathname
532 });
533}
534
535function stripBasename(basename, location) {
536 if (!basename) return location;
537 var base = addLeadingSlash(basename);
538 if (location.pathname.indexOf(base) !== 0) return location;
539 return _extends({}, location, {
540 pathname: location.pathname.substr(base.length)
541 });
542}
543
544function createURL(location) {
545 return typeof location === "string" ? location : history.createPath(location);
546}
547
548function staticHandler(methodName) {
549 return function () {
550 invariant(false, "You cannot %s with <StaticRouter>", methodName);
551 };
552}
553
554function noop() {}
555/**
556 * The public top-level API for a "static" <Router>, so-called because it
557 * can't actually change the current location. Instead, it just records
558 * location changes in a context object. Useful mainly in testing and
559 * server-rendering scenarios.
560 */
561
562
563var StaticRouter =
564/*#__PURE__*/
565function (_React$Component) {
566 _inheritsLoose(StaticRouter, _React$Component);
567
568 function StaticRouter() {
569 var _this;
570
571 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
572 args[_key] = arguments[_key];
573 }
574
575 _this = _React$Component.call.apply(_React$Component, [this].concat(args)) || this;
576
577 _this.handlePush = function (location) {
578 return _this.navigateTo(location, "PUSH");
579 };
580
581 _this.handleReplace = function (location) {
582 return _this.navigateTo(location, "REPLACE");
583 };
584
585 _this.handleListen = function () {
586 return noop;
587 };
588
589 _this.handleBlock = function () {
590 return noop;
591 };
592
593 return _this;
594 }
595
596 var _proto = StaticRouter.prototype;
597
598 _proto.navigateTo = function navigateTo(location, action) {
599 var _this$props = this.props,
600 _this$props$basename = _this$props.basename,
601 basename = _this$props$basename === void 0 ? "" : _this$props$basename,
602 context = _this$props.context;
603 context.action = action;
604 context.location = addBasename(basename, history.createLocation(location));
605 context.url = createURL(context.location);
606 };
607
608 _proto.render = function render() {
609 var _this$props2 = this.props,
610 _this$props2$basename = _this$props2.basename,
611 basename = _this$props2$basename === void 0 ? "" : _this$props2$basename,
612 _this$props2$context = _this$props2.context,
613 context = _this$props2$context === void 0 ? {} : _this$props2$context,
614 _this$props2$location = _this$props2.location,
615 location = _this$props2$location === void 0 ? "/" : _this$props2$location,
616 rest = _objectWithoutPropertiesLoose(_this$props2, ["basename", "context", "location"]);
617
618 var history$$1 = {
619 createHref: function createHref(path) {
620 return addLeadingSlash(basename + createURL(path));
621 },
622 action: "POP",
623 location: stripBasename(basename, history.createLocation(location)),
624 push: this.handlePush,
625 replace: this.handleReplace,
626 go: staticHandler("go"),
627 goBack: staticHandler("goBack"),
628 goForward: staticHandler("goForward"),
629 listen: this.handleListen,
630 block: this.handleBlock
631 };
632 return React.createElement(Router, _extends({}, rest, {
633 history: history$$1,
634 staticContext: context
635 }));
636 };
637
638 return StaticRouter;
639}(React.Component);
640
641{
642 StaticRouter.propTypes = {
643 basename: PropTypes.string,
644 context: PropTypes.object,
645 location: PropTypes.oneOfType([PropTypes.string, PropTypes.object])
646 };
647
648 StaticRouter.prototype.componentDidMount = function () {
649 warning(!this.props.history, "<StaticRouter> ignores the history prop. To use a custom history, " + "use `import { Router }` instead of `import { StaticRouter as Router }`.");
650 };
651}
652
653/**
654 * The public API for rendering the first <Route> that matches.
655 */
656
657var Switch =
658/*#__PURE__*/
659function (_React$Component) {
660 _inheritsLoose(Switch, _React$Component);
661
662 function Switch() {
663 return _React$Component.apply(this, arguments) || this;
664 }
665
666 var _proto = Switch.prototype;
667
668 _proto.render = function render() {
669 var _this = this;
670
671 return React.createElement(context.Consumer, null, function (context$$1) {
672 !context$$1 ? invariant(false, "You should not use <Switch> outside a <Router>") : void 0;
673 var location = _this.props.location || context$$1.location;
674 var element, match; // We use React.Children.forEach instead of React.Children.toArray().find()
675 // here because toArray adds keys to all child elements and we do not want
676 // to trigger an unmount/remount for two <Route>s that render the same
677 // component at different URLs.
678
679 React.Children.forEach(_this.props.children, function (child) {
680 if (match == null && React.isValidElement(child)) {
681 element = child;
682 var path = child.props.path || child.props.from;
683 match = path ? matchPath(location.pathname, _extends({}, child.props, {
684 path: path
685 })) : context$$1.match;
686 }
687 });
688 return match ? React.cloneElement(element, {
689 location: location,
690 computedMatch: match
691 }) : null;
692 });
693 };
694
695 return Switch;
696}(React.Component);
697
698{
699 Switch.propTypes = {
700 children: PropTypes.node,
701 location: PropTypes.object
702 };
703
704 Switch.prototype.componentDidUpdate = function (prevProps) {
705 warning(!(this.props.location && !prevProps.location), '<Switch> elements should not change from uncontrolled to controlled (or vice versa). You initially used no "location" prop and then provided one on a subsequent render.');
706 warning(!(!this.props.location && prevProps.location), '<Switch> elements should not change from controlled to uncontrolled (or vice versa). You provided a "location" prop initially but omitted it on a subsequent render.');
707 };
708}
709
710/**
711 * A public higher-order component to access the imperative API
712 */
713
714function withRouter(Component) {
715 var C = function C(props) {
716 var wrappedComponentRef = props.wrappedComponentRef,
717 remainingProps = _objectWithoutPropertiesLoose(props, ["wrappedComponentRef"]);
718
719 return React.createElement(Route, {
720 children: function children(routeComponentProps) {
721 return React.createElement(Component, _extends({}, remainingProps, routeComponentProps, {
722 ref: wrappedComponentRef
723 }));
724 }
725 });
726 };
727
728 C.displayName = "withRouter(" + (Component.displayName || Component.name) + ")";
729 C.WrappedComponent = Component;
730
731 {
732 C.propTypes = {
733 wrappedComponentRef: PropTypes.func
734 };
735 }
736
737 return hoistStatics(C, Component);
738}
739
740{
741 if (typeof window !== "undefined") {
742 var global = window;
743 var key = "__react_router_build__";
744 var buildNames = {
745 cjs: "CommonJS",
746 esm: "ES modules",
747 umd: "UMD"
748 };
749
750 if (global[key] && global[key] !== "cjs") {
751 var initialBuildName = buildNames[global[key]];
752 var secondaryBuildName = buildNames["cjs"]; // TODO: Add link to article that explains in detail how to avoid
753 // loading 2 different builds.
754
755 throw new Error("You are loading the " + secondaryBuildName + " build of React Router " + ("on a page that is already running the " + initialBuildName + " ") + "build, so things won't work right.");
756 }
757
758 global[key] = "cjs";
759 }
760}
761
762exports.MemoryRouter = MemoryRouter;
763exports.Prompt = Prompt;
764exports.Redirect = Redirect;
765exports.Route = Route;
766exports.Router = Router;
767exports.StaticRouter = StaticRouter;
768exports.Switch = Switch;
769exports.generatePath = generatePath;
770exports.matchPath = matchPath;
771exports.withRouter = withRouter;
772exports.__RouterContext = context;