UNPKG

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