1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | 'use strict';
|
7 |
|
8 | Object.defineProperty(exports, '__esModule', { value: true });
|
9 |
|
10 | var vue = require('vue');
|
11 | var devtoolsApi = require('@vue/devtools-api');
|
12 |
|
13 | const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
|
14 | const PolySymbol = (name) =>
|
15 |
|
16 | hasSymbol
|
17 | ? Symbol('[vue-router]: ' + name )
|
18 | : ('[vue-router]: ' ) + name;
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | const matchedRouteKey = PolySymbol('router view location matched' );
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | const viewDepthKey = PolySymbol('router view depth' );
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | const routerKey = PolySymbol('router' );
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
47 |
|
48 | const routeLocationKey = PolySymbol('route location' );
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | const routerViewLocationKey = PolySymbol('router view location' );
|
56 |
|
57 | const isBrowser = typeof window !== 'undefined';
|
58 |
|
59 | function isESModule(obj) {
|
60 | return obj.__esModule || (hasSymbol && obj[Symbol.toStringTag] === 'Module');
|
61 | }
|
62 | const assign = Object.assign;
|
63 | function applyToParams(fn, params) {
|
64 | const newParams = {};
|
65 | for (const key in params) {
|
66 | const value = params[key];
|
67 | newParams[key] = Array.isArray(value) ? value.map(fn) : fn(value);
|
68 | }
|
69 | return newParams;
|
70 | }
|
71 | const noop = () => { };
|
72 |
|
73 | function warn(msg) {
|
74 |
|
75 | const args = Array.from(arguments).slice(1);
|
76 | console.warn.apply(console, ['[Vue Router warn]: ' + msg].concat(args));
|
77 | }
|
78 |
|
79 | const TRAILING_SLASH_RE = /\/$/;
|
80 | const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | function parseURL(parseQuery, location, currentLocation = '/') {
|
91 | let path, query = {}, searchString = '', hash = '';
|
92 |
|
93 | const searchPos = location.indexOf('?');
|
94 | const hashPos = location.indexOf('#', searchPos > -1 ? searchPos : 0);
|
95 | if (searchPos > -1) {
|
96 | path = location.slice(0, searchPos);
|
97 | searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length);
|
98 | query = parseQuery(searchString);
|
99 | }
|
100 | if (hashPos > -1) {
|
101 | path = path || location.slice(0, hashPos);
|
102 |
|
103 | hash = location.slice(hashPos, location.length);
|
104 | }
|
105 |
|
106 | path = resolveRelativePath(path != null ? path : location, currentLocation);
|
107 |
|
108 | return {
|
109 | fullPath: path + (searchString && '?') + searchString + hash,
|
110 | path,
|
111 | query,
|
112 | hash,
|
113 | };
|
114 | }
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 | function stringifyURL(stringifyQuery, location) {
|
122 | const query = location.query ? stringifyQuery(location.query) : '';
|
123 | return location.path + (query && '?') + query + (location.hash || '');
|
124 | }
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | function stripBase(pathname, base) {
|
133 |
|
134 | if (!base || !pathname.toLowerCase().startsWith(base.toLowerCase()))
|
135 | return pathname;
|
136 | return pathname.slice(base.length) || '/';
|
137 | }
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 |
|
144 |
|
145 |
|
146 | function isSameRouteLocation(stringifyQuery, a, b) {
|
147 | const aLastIndex = a.matched.length - 1;
|
148 | const bLastIndex = b.matched.length - 1;
|
149 | return (aLastIndex > -1 &&
|
150 | aLastIndex === bLastIndex &&
|
151 | isSameRouteRecord(a.matched[aLastIndex], b.matched[bLastIndex]) &&
|
152 | isSameRouteLocationParams(a.params, b.params) &&
|
153 | stringifyQuery(a.query) === stringifyQuery(b.query) &&
|
154 | a.hash === b.hash);
|
155 | }
|
156 |
|
157 |
|
158 |
|
159 |
|
160 |
|
161 |
|
162 |
|
163 | function isSameRouteRecord(a, b) {
|
164 |
|
165 |
|
166 |
|
167 | return (a.aliasOf || a) === (b.aliasOf || b);
|
168 | }
|
169 | function isSameRouteLocationParams(a, b) {
|
170 | if (Object.keys(a).length !== Object.keys(b).length)
|
171 | return false;
|
172 | for (const key in a) {
|
173 | if (!isSameRouteLocationParamsValue(a[key], b[key]))
|
174 | return false;
|
175 | }
|
176 | return true;
|
177 | }
|
178 | function isSameRouteLocationParamsValue(a, b) {
|
179 | return Array.isArray(a)
|
180 | ? isEquivalentArray(a, b)
|
181 | : Array.isArray(b)
|
182 | ? isEquivalentArray(b, a)
|
183 | : a === b;
|
184 | }
|
185 |
|
186 |
|
187 |
|
188 |
|
189 |
|
190 |
|
191 |
|
192 | function isEquivalentArray(a, b) {
|
193 | return Array.isArray(b)
|
194 | ? a.length === b.length && a.every((value, i) => value === b[i])
|
195 | : a.length === 1 && a[0] === b;
|
196 | }
|
197 |
|
198 |
|
199 |
|
200 |
|
201 |
|
202 |
|
203 | function resolveRelativePath(to, from) {
|
204 | if (to.startsWith('/'))
|
205 | return to;
|
206 | if (!from.startsWith('/')) {
|
207 | warn(`Cannot resolve a relative location without an absolute path. Trying to resolve "${to}" from "${from}". It should look like "/${from}".`);
|
208 | return to;
|
209 | }
|
210 | if (!to)
|
211 | return from;
|
212 | const fromSegments = from.split('/');
|
213 | const toSegments = to.split('/');
|
214 | let position = fromSegments.length - 1;
|
215 | let toPosition;
|
216 | let segment;
|
217 | for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
|
218 | segment = toSegments[toPosition];
|
219 |
|
220 | if (position === 1 || segment === '.')
|
221 | continue;
|
222 | if (segment === '..')
|
223 | position--;
|
224 |
|
225 | else
|
226 | break;
|
227 | }
|
228 | return (fromSegments.slice(0, position).join('/') +
|
229 | '/' +
|
230 | toSegments
|
231 | .slice(toPosition - (toPosition === toSegments.length ? 1 : 0))
|
232 | .join('/'));
|
233 | }
|
234 |
|
235 | var NavigationType;
|
236 | (function (NavigationType) {
|
237 | NavigationType["pop"] = "pop";
|
238 | NavigationType["push"] = "push";
|
239 | })(NavigationType || (NavigationType = {}));
|
240 | var NavigationDirection;
|
241 | (function (NavigationDirection) {
|
242 | NavigationDirection["back"] = "back";
|
243 | NavigationDirection["forward"] = "forward";
|
244 | NavigationDirection["unknown"] = "";
|
245 | })(NavigationDirection || (NavigationDirection = {}));
|
246 |
|
247 |
|
248 |
|
249 | const START = '';
|
250 |
|
251 |
|
252 |
|
253 |
|
254 |
|
255 |
|
256 |
|
257 | function normalizeBase(base) {
|
258 | if (!base) {
|
259 | if (isBrowser) {
|
260 |
|
261 | const baseEl = document.querySelector('base');
|
262 | base = (baseEl && baseEl.getAttribute('href')) || '/';
|
263 |
|
264 | base = base.replace(/^\w+:\/\/[^\/]+/, '');
|
265 | }
|
266 | else {
|
267 | base = '/';
|
268 | }
|
269 | }
|
270 |
|
271 |
|
272 |
|
273 | if (base[0] !== '/' && base[0] !== '#')
|
274 | base = '/' + base;
|
275 |
|
276 |
|
277 | return removeTrailingSlash(base);
|
278 | }
|
279 |
|
280 | const BEFORE_HASH_RE = /^[^#]+#/;
|
281 | function createHref(base, location) {
|
282 | return base.replace(BEFORE_HASH_RE, '#') + location;
|
283 | }
|
284 |
|
285 | function getElementPosition(el, offset) {
|
286 | const docRect = document.documentElement.getBoundingClientRect();
|
287 | const elRect = el.getBoundingClientRect();
|
288 | return {
|
289 | behavior: offset.behavior,
|
290 | left: elRect.left - docRect.left - (offset.left || 0),
|
291 | top: elRect.top - docRect.top - (offset.top || 0),
|
292 | };
|
293 | }
|
294 | const computeScrollPosition = () => ({
|
295 | left: window.pageXOffset,
|
296 | top: window.pageYOffset,
|
297 | });
|
298 | function scrollToPosition(position) {
|
299 | let scrollToOptions;
|
300 | if ('el' in position) {
|
301 | const positionEl = position.el;
|
302 | const isIdSelector = typeof positionEl === 'string' && positionEl.startsWith('#');
|
303 | |
304 |
|
305 |
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 |
|
324 | if (typeof position.el === 'string') {
|
325 | if (!isIdSelector || !document.getElementById(position.el.slice(1))) {
|
326 | try {
|
327 | const foundEl = document.querySelector(position.el);
|
328 | if (isIdSelector && foundEl) {
|
329 | warn(`The selector "${position.el}" should be passed as "el: document.querySelector('${position.el}')" because it starts with "#".`);
|
330 |
|
331 | return;
|
332 | }
|
333 | }
|
334 | catch (err) {
|
335 | warn(`The selector "${position.el}" is invalid. If you are using an id selector, make sure to escape it. You can find more information about escaping characters in selectors at https://mathiasbynens.be/notes/css-escapes or use CSS.escape (https://developer.mozilla.org/en-US/docs/Web/API/CSS/escape).`);
|
336 |
|
337 | return;
|
338 | }
|
339 | }
|
340 | }
|
341 | const el = typeof positionEl === 'string'
|
342 | ? isIdSelector
|
343 | ? document.getElementById(positionEl.slice(1))
|
344 | : document.querySelector(positionEl)
|
345 | : positionEl;
|
346 | if (!el) {
|
347 | warn(`Couldn't find element using selector "${position.el}" returned by scrollBehavior.`);
|
348 | return;
|
349 | }
|
350 | scrollToOptions = getElementPosition(el, position);
|
351 | }
|
352 | else {
|
353 | scrollToOptions = position;
|
354 | }
|
355 | if ('scrollBehavior' in document.documentElement.style)
|
356 | window.scrollTo(scrollToOptions);
|
357 | else {
|
358 | window.scrollTo(scrollToOptions.left != null ? scrollToOptions.left : window.pageXOffset, scrollToOptions.top != null ? scrollToOptions.top : window.pageYOffset);
|
359 | }
|
360 | }
|
361 | function getScrollKey(path, delta) {
|
362 | const position = history.state ? history.state.position - delta : -1;
|
363 | return position + path;
|
364 | }
|
365 | const scrollPositions = new Map();
|
366 | function saveScrollPosition(key, scrollPosition) {
|
367 | scrollPositions.set(key, scrollPosition);
|
368 | }
|
369 | function getSavedScrollPosition(key) {
|
370 | const scroll = scrollPositions.get(key);
|
371 |
|
372 | scrollPositions.delete(key);
|
373 | return scroll;
|
374 | }
|
375 |
|
376 |
|
377 |
|
378 |
|
379 |
|
380 |
|
381 |
|
382 |
|
383 |
|
384 |
|
385 |
|
386 |
|
387 |
|
388 |
|
389 |
|
390 |
|
391 | let createBaseLocation = () => location.protocol + '//' + location.host;
|
392 |
|
393 |
|
394 |
|
395 |
|
396 | function createCurrentLocation(base, location) {
|
397 | const { pathname, search, hash } = location;
|
398 |
|
399 | const hashPos = base.indexOf('#');
|
400 | if (hashPos > -1) {
|
401 | let slicePos = hash.includes(base.slice(hashPos))
|
402 | ? base.slice(hashPos).length
|
403 | : 1;
|
404 | let pathFromHash = hash.slice(slicePos);
|
405 |
|
406 | if (pathFromHash[0] !== '/')
|
407 | pathFromHash = '/' + pathFromHash;
|
408 | return stripBase(pathFromHash, '');
|
409 | }
|
410 | const path = stripBase(pathname, base);
|
411 | return path + search + hash;
|
412 | }
|
413 | function useHistoryListeners(base, historyState, currentLocation, replace) {
|
414 | let listeners = [];
|
415 | let teardowns = [];
|
416 |
|
417 |
|
418 | let pauseState = null;
|
419 | const popStateHandler = ({ state, }) => {
|
420 | const to = createCurrentLocation(base, location);
|
421 | const from = currentLocation.value;
|
422 | const fromState = historyState.value;
|
423 | let delta = 0;
|
424 | if (state) {
|
425 | currentLocation.value = to;
|
426 | historyState.value = state;
|
427 |
|
428 | if (pauseState && pauseState === from) {
|
429 | pauseState = null;
|
430 | return;
|
431 | }
|
432 | delta = fromState ? state.position - fromState.position : 0;
|
433 | }
|
434 | else {
|
435 | replace(to);
|
436 | }
|
437 |
|
438 |
|
439 |
|
440 |
|
441 |
|
442 |
|
443 | listeners.forEach(listener => {
|
444 | listener(currentLocation.value, from, {
|
445 | delta,
|
446 | type: NavigationType.pop,
|
447 | direction: delta
|
448 | ? delta > 0
|
449 | ? NavigationDirection.forward
|
450 | : NavigationDirection.back
|
451 | : NavigationDirection.unknown,
|
452 | });
|
453 | });
|
454 | };
|
455 | function pauseListeners() {
|
456 | pauseState = currentLocation.value;
|
457 | }
|
458 | function listen(callback) {
|
459 |
|
460 | listeners.push(callback);
|
461 | const teardown = () => {
|
462 | const index = listeners.indexOf(callback);
|
463 | if (index > -1)
|
464 | listeners.splice(index, 1);
|
465 | };
|
466 | teardowns.push(teardown);
|
467 | return teardown;
|
468 | }
|
469 | function beforeUnloadListener() {
|
470 | const { history } = window;
|
471 | if (!history.state)
|
472 | return;
|
473 | history.replaceState(assign({}, history.state, { scroll: computeScrollPosition() }), '');
|
474 | }
|
475 | function destroy() {
|
476 | for (const teardown of teardowns)
|
477 | teardown();
|
478 | teardowns = [];
|
479 | window.removeEventListener('popstate', popStateHandler);
|
480 | window.removeEventListener('beforeunload', beforeUnloadListener);
|
481 | }
|
482 |
|
483 | window.addEventListener('popstate', popStateHandler);
|
484 | window.addEventListener('beforeunload', beforeUnloadListener);
|
485 | return {
|
486 | pauseListeners,
|
487 | listen,
|
488 | destroy,
|
489 | };
|
490 | }
|
491 |
|
492 |
|
493 |
|
494 | function buildState(back, current, forward, replaced = false, computeScroll = false) {
|
495 | return {
|
496 | back,
|
497 | current,
|
498 | forward,
|
499 | replaced,
|
500 | position: window.history.length,
|
501 | scroll: computeScroll ? computeScrollPosition() : null,
|
502 | };
|
503 | }
|
504 | function useHistoryStateNavigation(base) {
|
505 | const { history, location } = window;
|
506 |
|
507 | const currentLocation = {
|
508 | value: createCurrentLocation(base, location),
|
509 | };
|
510 | const historyState = { value: history.state };
|
511 |
|
512 | if (!historyState.value) {
|
513 | changeLocation(currentLocation.value, {
|
514 | back: null,
|
515 | current: currentLocation.value,
|
516 | forward: null,
|
517 |
|
518 | position: history.length - 1,
|
519 | replaced: true,
|
520 |
|
521 |
|
522 | scroll: null,
|
523 | }, true);
|
524 | }
|
525 | function changeLocation(to, state, replace) {
|
526 | |
527 |
|
528 |
|
529 |
|
530 |
|
531 |
|
532 |
|
533 |
|
534 |
|
535 | const hashIndex = base.indexOf('#');
|
536 | const url = hashIndex > -1
|
537 | ? (location.host && document.querySelector('base')
|
538 | ? base
|
539 | : base.slice(hashIndex)) + to
|
540 | : createBaseLocation() + base + to;
|
541 | try {
|
542 |
|
543 |
|
544 | history[replace ? 'replaceState' : 'pushState'](state, '', url);
|
545 | historyState.value = state;
|
546 | }
|
547 | catch (err) {
|
548 | {
|
549 | warn('Error with push/replace State', err);
|
550 | }
|
551 |
|
552 | location[replace ? 'replace' : 'assign'](url);
|
553 | }
|
554 | }
|
555 | function replace(to, data) {
|
556 | const state = assign({}, history.state, buildState(historyState.value.back,
|
557 |
|
558 | to, historyState.value.forward, true), data, { position: historyState.value.position });
|
559 | changeLocation(to, state, true);
|
560 | currentLocation.value = to;
|
561 | }
|
562 | function push(to, data) {
|
563 |
|
564 |
|
565 | const currentState = assign({},
|
566 |
|
567 |
|
568 |
|
569 | historyState.value, history.state, {
|
570 | forward: to,
|
571 | scroll: computeScrollPosition(),
|
572 | });
|
573 | if (!history.state) {
|
574 | warn(`history.state seems to have been manually replaced without preserving the necessary values. Make sure to preserve existing history state if you are manually calling history.replaceState:\n\n` +
|
575 | `history.replaceState(history.state, '', url)\n\n` +
|
576 | `You can find more information at https://next.router.vuejs.org/guide/migration/#usage-of-history-state.`);
|
577 | }
|
578 | changeLocation(currentState.current, currentState, true);
|
579 | const state = assign({}, buildState(currentLocation.value, to, null), { position: currentState.position + 1 }, data);
|
580 | changeLocation(to, state, false);
|
581 | currentLocation.value = to;
|
582 | }
|
583 | return {
|
584 | location: currentLocation,
|
585 | state: historyState,
|
586 | push,
|
587 | replace,
|
588 | };
|
589 | }
|
590 |
|
591 |
|
592 |
|
593 |
|
594 |
|
595 | function createWebHistory(base) {
|
596 | base = normalizeBase(base);
|
597 | const historyNavigation = useHistoryStateNavigation(base);
|
598 | const historyListeners = useHistoryListeners(base, historyNavigation.state, historyNavigation.location, historyNavigation.replace);
|
599 | function go(delta, triggerListeners = true) {
|
600 | if (!triggerListeners)
|
601 | historyListeners.pauseListeners();
|
602 | history.go(delta);
|
603 | }
|
604 | const routerHistory = assign({
|
605 |
|
606 | location: '',
|
607 | base,
|
608 | go,
|
609 | createHref: createHref.bind(null, base),
|
610 | }, historyNavigation, historyListeners);
|
611 | Object.defineProperty(routerHistory, 'location', {
|
612 | enumerable: true,
|
613 | get: () => historyNavigation.location.value,
|
614 | });
|
615 | Object.defineProperty(routerHistory, 'state', {
|
616 | enumerable: true,
|
617 | get: () => historyNavigation.state.value,
|
618 | });
|
619 | return routerHistory;
|
620 | }
|
621 |
|
622 | /**
|
623 | * Creates a in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
|
624 | * It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`.
|
625 | *
|
626 | * @param base - Base applied to all urls, defaults to '/'
|
627 | * @returns a history object that can be passed to the router constructor
|
628 | */
|
629 | function createMemoryHistory(base = '') {
|
630 | let listeners = [];
|
631 | let queue = [START];
|
632 | let position = 0;
|
633 | base = normalizeBase(base);
|
634 | function setLocation(location) {
|
635 | position++;
|
636 | if (position === queue.length) {
|
637 |
|
638 | queue.push(location);
|
639 | }
|
640 | else {
|
641 |
|
642 | queue.splice(position);
|
643 | queue.push(location);
|
644 | }
|
645 | }
|
646 | function triggerListeners(to, from, { direction, delta }) {
|
647 | const info = {
|
648 | direction,
|
649 | delta,
|
650 | type: NavigationType.pop,
|
651 | };
|
652 | for (const callback of listeners) {
|
653 | callback(to, from, info);
|
654 | }
|
655 | }
|
656 | const routerHistory = {
|
657 |
|
658 | location: START,
|
659 |
|
660 | state: {},
|
661 | base,
|
662 | createHref: createHref.bind(null, base),
|
663 | replace(to) {
|
664 |
|
665 | queue.splice(position--, 1);
|
666 | setLocation(to);
|
667 | },
|
668 | push(to, data) {
|
669 | setLocation(to);
|
670 | },
|
671 | listen(callback) {
|
672 | listeners.push(callback);
|
673 | return () => {
|
674 | const index = listeners.indexOf(callback);
|
675 | if (index > -1)
|
676 | listeners.splice(index, 1);
|
677 | };
|
678 | },
|
679 | destroy() {
|
680 | listeners = [];
|
681 | queue = [START];
|
682 | position = 0;
|
683 | },
|
684 | go(delta, shouldTrigger = true) {
|
685 | const from = this.location;
|
686 | const direction =
|
687 |
|
688 |
|
689 |
|
690 | delta < 0 ? NavigationDirection.back : NavigationDirection.forward;
|
691 | position = Math.max(0, Math.min(position + delta, queue.length - 1));
|
692 | if (shouldTrigger) {
|
693 | triggerListeners(this.location, from, {
|
694 | direction,
|
695 | delta,
|
696 | });
|
697 | }
|
698 | },
|
699 | };
|
700 | Object.defineProperty(routerHistory, 'location', {
|
701 | enumerable: true,
|
702 | get: () => queue[position],
|
703 | });
|
704 | return routerHistory;
|
705 | }
|
706 |
|
707 | /**
|
708 | * Creates a hash history. Useful for web applications with no host (e.g.
|
709 | * `file://`) or when configuring a server to handle any URL is not possible.
|
710 | *
|
711 | * @param base - optional base to provide. Defaults to `location.pathname +
|
712 | * location.search` If there is a `<base>` tag in the `head`, its value will be
|
713 | * ignored in favor of this parameter **but note it affects all the
|
714 | * history.pushState() calls**, meaning that if you use a `<base>` tag, it's
|
715 | * `href` value **has to match this parameter** (ignoring anything after the
|
716 | * `#`).
|
717 | *
|
718 | * @example
|
719 | * ```js
|
720 | * // at https://example.com/folder
|
721 | * createWebHashHistory() // gives a url of `https://example.com/folder#`
|
722 | * createWebHashHistory('/folder/') // gives a url of `https://example.com/folder/#`
|
723 | * // if the `#` is provided in the base, it won't be added by `createWebHashHistory`
|
724 | * createWebHashHistory('/folder/#/app/') // gives a url of `https://example.com/folder/#/app/`
|
725 | * // you should avoid doing this because it changes the original url and breaks copying urls
|
726 | * createWebHashHistory('/other-folder/') // gives a url of `https://example.com/other-folder/#`
|
727 | *
|
728 | * // at file:///usr/etc/folder/index.html
|
729 | * // for locations with no `host`, the base is ignored
|
730 | * createWebHashHistory('/iAmIgnored') // gives a url of `file:///usr/etc/folder/index.html#`
|
731 | * ```
|
732 | */
|
733 | function createWebHashHistory(base) {
|
734 |
|
735 |
|
736 |
|
737 | base = location.host ? base || location.pathname + location.search : '';
|
738 |
|
739 | if (!base.includes('#'))
|
740 | base += '#';
|
741 | if (!base.endsWith('#/') && !base.endsWith('#')) {
|
742 | warn(`A hash base must end with a "#":\n"${base}" should be "${base.replace(/#.*$/, '#')}".`);
|
743 | }
|
744 | return createWebHistory(base);
|
745 | }
|
746 |
|
747 | function isRouteLocation(route) {
|
748 | return typeof route === 'string' || (route && typeof route === 'object');
|
749 | }
|
750 | function isRouteName(name) {
|
751 | return typeof name === 'string' || typeof name === 'symbol';
|
752 | }
|
753 |
|
754 |
|
755 |
|
756 |
|
757 |
|
758 |
|
759 |
|
760 |
|
761 |
|
762 |
|
763 |
|
764 |
|
765 |
|
766 |
|
767 |
|
768 |
|
769 | const START_LOCATION_NORMALIZED = {
|
770 | path: '/',
|
771 | name: undefined,
|
772 | params: {},
|
773 | query: {},
|
774 | hash: '',
|
775 | fullPath: '/',
|
776 | matched: [],
|
777 | meta: {},
|
778 | redirectedFrom: undefined,
|
779 | };
|
780 |
|
781 | const NavigationFailureSymbol = PolySymbol('navigation failure' );
|
782 |
|
783 |
|
784 |
|
785 |
|
786 | exports.NavigationFailureType = void 0;
|
787 | (function (NavigationFailureType) {
|
788 | |
789 |
|
790 |
|
791 |
|
792 | NavigationFailureType[NavigationFailureType["aborted"] = 4] = "aborted";
|
793 | |
794 |
|
795 |
|
796 |
|
797 | NavigationFailureType[NavigationFailureType["cancelled"] = 8] = "cancelled";
|
798 | |
799 |
|
800 |
|
801 |
|
802 | NavigationFailureType[NavigationFailureType["duplicated"] = 16] = "duplicated";
|
803 | })(exports.NavigationFailureType || (exports.NavigationFailureType = {}));
|
804 |
|
805 | const ErrorTypeMessages = {
|
806 | [1 ]({ location, currentLocation }) {
|
807 | return `No match for\n ${JSON.stringify(location)}${currentLocation
|
808 | ? '\nwhile being at\n' + JSON.stringify(currentLocation)
|
809 | : ''}`;
|
810 | },
|
811 | [2 ]({ from, to, }) {
|
812 | return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
|
813 | },
|
814 | [4 ]({ from, to }) {
|
815 | return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
|
816 | },
|
817 | [8 ]({ from, to }) {
|
818 | return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
|
819 | },
|
820 | [16 ]({ from, to }) {
|
821 | return `Avoided redundant navigation to current location: "${from.fullPath}".`;
|
822 | },
|
823 | };
|
824 | function createRouterError(type, params) {
|
825 |
|
826 | {
|
827 | return assign(new Error(ErrorTypeMessages[type](params)), {
|
828 | type,
|
829 | [NavigationFailureSymbol]: true,
|
830 | }, params);
|
831 | }
|
832 | }
|
833 | function isNavigationFailure(error, type) {
|
834 | return (error instanceof Error &&
|
835 | NavigationFailureSymbol in error &&
|
836 | (type == null || !!(error.type & type)));
|
837 | }
|
838 | const propertiesToLog = ['params', 'query', 'hash'];
|
839 | function stringifyRoute(to) {
|
840 | if (typeof to === 'string')
|
841 | return to;
|
842 | if ('path' in to)
|
843 | return to.path;
|
844 | const location = {};
|
845 | for (const key of propertiesToLog) {
|
846 | if (key in to)
|
847 | location[key] = to[key];
|
848 | }
|
849 | return JSON.stringify(location, null, 2);
|
850 | }
|
851 |
|
852 |
|
853 | const BASE_PARAM_PATTERN = '[^/]+?';
|
854 | const BASE_PATH_PARSER_OPTIONS = {
|
855 | sensitive: false,
|
856 | strict: false,
|
857 | start: true,
|
858 | end: true,
|
859 | };
|
860 |
|
861 | const REGEX_CHARS_RE = /[.+*?^${}()[\]/\\]/g;
|
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 |
|
869 | function tokensToParser(segments, extraOptions) {
|
870 | const options = assign({}, BASE_PATH_PARSER_OPTIONS, extraOptions);
|
871 |
|
872 | const score = [];
|
873 |
|
874 | let pattern = options.start ? '^' : '';
|
875 |
|
876 | const keys = [];
|
877 | for (const segment of segments) {
|
878 |
|
879 | const segmentScores = segment.length ? [] : [90 ];
|
880 |
|
881 | if (options.strict && !segment.length)
|
882 | pattern += '/';
|
883 | for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
|
884 | const token = segment[tokenIndex];
|
885 |
|
886 | let subSegmentScore = 40 +
|
887 | (options.sensitive ? 0.25 : 0);
|
888 | if (token.type === 0 ) {
|
889 |
|
890 | if (!tokenIndex)
|
891 | pattern += '/';
|
892 | pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
|
893 | subSegmentScore += 40 ;
|
894 | }
|
895 | else if (token.type === 1 ) {
|
896 | const { value, repeatable, optional, regexp } = token;
|
897 | keys.push({
|
898 | name: value,
|
899 | repeatable,
|
900 | optional,
|
901 | });
|
902 | const re = regexp ? regexp : BASE_PARAM_PATTERN;
|
903 |
|
904 | if (re !== BASE_PARAM_PATTERN) {
|
905 | subSegmentScore += 10 ;
|
906 |
|
907 | try {
|
908 | new RegExp(`(${re})`);
|
909 | }
|
910 | catch (err) {
|
911 | throw new Error(`Invalid custom RegExp for param "${value}" (${re}): ` +
|
912 | err.message);
|
913 | }
|
914 | }
|
915 |
|
916 | let subPattern = repeatable ? `((?:${re})(?:/(?:${re}))*)` : `(${re})`;
|
917 |
|
918 | if (!tokenIndex)
|
919 | subPattern =
|
920 |
|
921 |
|
922 | optional && segment.length < 2
|
923 | ? `(?:/${subPattern})`
|
924 | : '/' + subPattern;
|
925 | if (optional)
|
926 | subPattern += '?';
|
927 | pattern += subPattern;
|
928 | subSegmentScore += 20 ;
|
929 | if (optional)
|
930 | subSegmentScore += -8 ;
|
931 | if (repeatable)
|
932 | subSegmentScore += -20 ;
|
933 | if (re === '.*')
|
934 | subSegmentScore += -50 ;
|
935 | }
|
936 | segmentScores.push(subSegmentScore);
|
937 | }
|
938 |
|
939 |
|
940 | score.push(segmentScores);
|
941 | }
|
942 |
|
943 | if (options.strict && options.end) {
|
944 | const i = score.length - 1;
|
945 | score[i][score[i].length - 1] += 0.7000000000000001 ;
|
946 | }
|
947 |
|
948 | if (!options.strict)
|
949 | pattern += '/?';
|
950 | if (options.end)
|
951 | pattern += '$';
|
952 |
|
953 | else if (options.strict)
|
954 | pattern += '(?:/|$)';
|
955 | const re = new RegExp(pattern, options.sensitive ? '' : 'i');
|
956 | function parse(path) {
|
957 | const match = path.match(re);
|
958 | const params = {};
|
959 | if (!match)
|
960 | return null;
|
961 | for (let i = 1; i < match.length; i++) {
|
962 | const value = match[i] || '';
|
963 | const key = keys[i - 1];
|
964 | params[key.name] = value && key.repeatable ? value.split('/') : value;
|
965 | }
|
966 | return params;
|
967 | }
|
968 | function stringify(params) {
|
969 | let path = '';
|
970 |
|
971 | let avoidDuplicatedSlash = false;
|
972 | for (const segment of segments) {
|
973 | if (!avoidDuplicatedSlash || !path.endsWith('/'))
|
974 | path += '/';
|
975 | avoidDuplicatedSlash = false;
|
976 | for (const token of segment) {
|
977 | if (token.type === 0 ) {
|
978 | path += token.value;
|
979 | }
|
980 | else if (token.type === 1 ) {
|
981 | const { value, repeatable, optional } = token;
|
982 | const param = value in params ? params[value] : '';
|
983 | if (Array.isArray(param) && !repeatable)
|
984 | throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
|
985 | const text = Array.isArray(param) ? param.join('/') : param;
|
986 | if (!text) {
|
987 | if (optional) {
|
988 |
|
989 |
|
990 | if (segment.length < 2 && segments.length > 1) {
|
991 |
|
992 | if (path.endsWith('/'))
|
993 | path = path.slice(0, -1);
|
994 |
|
995 | else
|
996 | avoidDuplicatedSlash = true;
|
997 | }
|
998 | }
|
999 | else
|
1000 | throw new Error(`Missing required param "${value}"`);
|
1001 | }
|
1002 | path += text;
|
1003 | }
|
1004 | }
|
1005 | }
|
1006 | return path;
|
1007 | }
|
1008 | return {
|
1009 | re,
|
1010 | score,
|
1011 | keys,
|
1012 | parse,
|
1013 | stringify,
|
1014 | };
|
1015 | }
|
1016 |
|
1017 |
|
1018 |
|
1019 |
|
1020 |
|
1021 |
|
1022 |
|
1023 |
|
1024 |
|
1025 | function compareScoreArray(a, b) {
|
1026 | let i = 0;
|
1027 | while (i < a.length && i < b.length) {
|
1028 | const diff = b[i] - a[i];
|
1029 |
|
1030 | if (diff)
|
1031 | return diff;
|
1032 | i++;
|
1033 | }
|
1034 |
|
1035 |
|
1036 | if (a.length < b.length) {
|
1037 | return a.length === 1 && a[0] === 40 + 40
|
1038 | ? -1
|
1039 | : 1;
|
1040 | }
|
1041 | else if (a.length > b.length) {
|
1042 | return b.length === 1 && b[0] === 40 + 40
|
1043 | ? 1
|
1044 | : -1;
|
1045 | }
|
1046 | return 0;
|
1047 | }
|
1048 |
|
1049 |
|
1050 |
|
1051 |
|
1052 |
|
1053 |
|
1054 |
|
1055 | function comparePathParserScore(a, b) {
|
1056 | let i = 0;
|
1057 | const aScore = a.score;
|
1058 | const bScore = b.score;
|
1059 | while (i < aScore.length && i < bScore.length) {
|
1060 | const comp = compareScoreArray(aScore[i], bScore[i]);
|
1061 |
|
1062 | if (comp)
|
1063 | return comp;
|
1064 | i++;
|
1065 | }
|
1066 |
|
1067 | return bScore.length - aScore.length;
|
1068 |
|
1069 |
|
1070 |
|
1071 |
|
1072 |
|
1073 |
|
1074 | }
|
1075 |
|
1076 | const ROOT_TOKEN = {
|
1077 | type: 0 ,
|
1078 | value: '',
|
1079 | };
|
1080 | const VALID_PARAM_RE = /[a-zA-Z0-9_]/;
|
1081 |
|
1082 |
|
1083 |
|
1084 | function tokenizePath(path) {
|
1085 | if (!path)
|
1086 | return [[]];
|
1087 | if (path === '/')
|
1088 | return [[ROOT_TOKEN]];
|
1089 | if (!path.startsWith('/')) {
|
1090 | throw new Error(`Route paths should start with a "/": "${path}" should be "/${path}".`
|
1091 | );
|
1092 | }
|
1093 |
|
1094 | function crash(message) {
|
1095 | throw new Error(`ERR (${state})/"${buffer}": ${message}`);
|
1096 | }
|
1097 | let state = 0 ;
|
1098 | let previousState = state;
|
1099 | const tokens = [];
|
1100 |
|
1101 |
|
1102 | let segment;
|
1103 | function finalizeSegment() {
|
1104 | if (segment)
|
1105 | tokens.push(segment);
|
1106 | segment = [];
|
1107 | }
|
1108 |
|
1109 | let i = 0;
|
1110 |
|
1111 | let char;
|
1112 |
|
1113 | let buffer = '';
|
1114 |
|
1115 | let customRe = '';
|
1116 | function consumeBuffer() {
|
1117 | if (!buffer)
|
1118 | return;
|
1119 | if (state === 0 ) {
|
1120 | segment.push({
|
1121 | type: 0 ,
|
1122 | value: buffer,
|
1123 | });
|
1124 | }
|
1125 | else if (state === 1 ||
|
1126 | state === 2 ||
|
1127 | state === 3 ) {
|
1128 | if (segment.length > 1 && (char === '*' || char === '+'))
|
1129 | crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
|
1130 | segment.push({
|
1131 | type: 1 ,
|
1132 | value: buffer,
|
1133 | regexp: customRe,
|
1134 | repeatable: char === '*' || char === '+',
|
1135 | optional: char === '*' || char === '?',
|
1136 | });
|
1137 | }
|
1138 | else {
|
1139 | crash('Invalid state to consume buffer');
|
1140 | }
|
1141 | buffer = '';
|
1142 | }
|
1143 | function addCharToBuffer() {
|
1144 | buffer += char;
|
1145 | }
|
1146 | while (i < path.length) {
|
1147 | char = path[i++];
|
1148 | if (char === '\\' && state !== 2 ) {
|
1149 | previousState = state;
|
1150 | state = 4 ;
|
1151 | continue;
|
1152 | }
|
1153 | switch (state) {
|
1154 | case 0 :
|
1155 | if (char === '/') {
|
1156 | if (buffer) {
|
1157 | consumeBuffer();
|
1158 | }
|
1159 | finalizeSegment();
|
1160 | }
|
1161 | else if (char === ':') {
|
1162 | consumeBuffer();
|
1163 | state = 1 ;
|
1164 | }
|
1165 | else {
|
1166 | addCharToBuffer();
|
1167 | }
|
1168 | break;
|
1169 | case 4 :
|
1170 | addCharToBuffer();
|
1171 | state = previousState;
|
1172 | break;
|
1173 | case 1 :
|
1174 | if (char === '(') {
|
1175 | state = 2 ;
|
1176 | }
|
1177 | else if (VALID_PARAM_RE.test(char)) {
|
1178 | addCharToBuffer();
|
1179 | }
|
1180 | else {
|
1181 | consumeBuffer();
|
1182 | state = 0 ;
|
1183 |
|
1184 | if (char !== '*' && char !== '?' && char !== '+')
|
1185 | i--;
|
1186 | }
|
1187 | break;
|
1188 | case 2 :
|
1189 |
|
1190 |
|
1191 |
|
1192 |
|
1193 |
|
1194 | if (char === ')') {
|
1195 |
|
1196 | if (customRe[customRe.length - 1] == '\\')
|
1197 | customRe = customRe.slice(0, -1) + char;
|
1198 | else
|
1199 | state = 3 ;
|
1200 | }
|
1201 | else {
|
1202 | customRe += char;
|
1203 | }
|
1204 | break;
|
1205 | case 3 :
|
1206 |
|
1207 | consumeBuffer();
|
1208 | state = 0 ;
|
1209 |
|
1210 | if (char !== '*' && char !== '?' && char !== '+')
|
1211 | i--;
|
1212 | customRe = '';
|
1213 | break;
|
1214 | default:
|
1215 | crash('Unknown state');
|
1216 | break;
|
1217 | }
|
1218 | }
|
1219 | if (state === 2 )
|
1220 | crash(`Unfinished custom RegExp for param "${buffer}"`);
|
1221 | consumeBuffer();
|
1222 | finalizeSegment();
|
1223 |
|
1224 | return tokens;
|
1225 | }
|
1226 |
|
1227 | function createRouteRecordMatcher(record, parent, options) {
|
1228 | const parser = tokensToParser(tokenizePath(record.path), options);
|
1229 |
|
1230 | {
|
1231 | const existingKeys = new Set();
|
1232 | for (const key of parser.keys) {
|
1233 | if (existingKeys.has(key.name))
|
1234 | warn(`Found duplicated params with name "${key.name}" for path "${record.path}". Only the last one will be available on "$route.params".`);
|
1235 | existingKeys.add(key.name);
|
1236 | }
|
1237 | }
|
1238 | const matcher = assign(parser, {
|
1239 | record,
|
1240 | parent,
|
1241 |
|
1242 | children: [],
|
1243 | alias: [],
|
1244 | });
|
1245 | if (parent) {
|
1246 |
|
1247 |
|
1248 |
|
1249 | if (!matcher.record.aliasOf === !parent.record.aliasOf)
|
1250 | parent.children.push(matcher);
|
1251 | }
|
1252 | return matcher;
|
1253 | }
|
1254 |
|
1255 |
|
1256 |
|
1257 |
|
1258 |
|
1259 |
|
1260 |
|
1261 |
|
1262 | function createRouterMatcher(routes, globalOptions) {
|
1263 |
|
1264 | const matchers = [];
|
1265 | const matcherMap = new Map();
|
1266 | globalOptions = mergeOptions({ strict: false, end: true, sensitive: false }, globalOptions);
|
1267 | function getRecordMatcher(name) {
|
1268 | return matcherMap.get(name);
|
1269 | }
|
1270 | function addRoute(record, parent, originalRecord) {
|
1271 |
|
1272 | const isRootAdd = !originalRecord;
|
1273 | const mainNormalizedRecord = normalizeRouteRecord(record);
|
1274 |
|
1275 | mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
|
1276 | const options = mergeOptions(globalOptions, record);
|
1277 |
|
1278 | const normalizedRecords = [
|
1279 | mainNormalizedRecord,
|
1280 | ];
|
1281 | if ('alias' in record) {
|
1282 | const aliases = typeof record.alias === 'string' ? [record.alias] : record.alias;
|
1283 | for (const alias of aliases) {
|
1284 | normalizedRecords.push(assign({}, mainNormalizedRecord, {
|
1285 |
|
1286 |
|
1287 | components: originalRecord
|
1288 | ? originalRecord.record.components
|
1289 | : mainNormalizedRecord.components,
|
1290 | path: alias,
|
1291 |
|
1292 | aliasOf: originalRecord
|
1293 | ? originalRecord.record
|
1294 | : mainNormalizedRecord,
|
1295 |
|
1296 |
|
1297 | }));
|
1298 | }
|
1299 | }
|
1300 | let matcher;
|
1301 | let originalMatcher;
|
1302 | for (const normalizedRecord of normalizedRecords) {
|
1303 | const { path } = normalizedRecord;
|
1304 |
|
1305 |
|
1306 |
|
1307 | if (parent && path[0] !== '/') {
|
1308 | const parentPath = parent.record.path;
|
1309 | const connectingSlash = parentPath[parentPath.length - 1] === '/' ? '' : '/';
|
1310 | normalizedRecord.path =
|
1311 | parent.record.path + (path && connectingSlash + path);
|
1312 | }
|
1313 | if (normalizedRecord.path === '*') {
|
1314 | throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\n' +
|
1315 | 'See more at https://next.router.vuejs.org/guide/migration/#removed-star-or-catch-all-routes.');
|
1316 | }
|
1317 |
|
1318 | matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
|
1319 | if (parent && path[0] === '/')
|
1320 | checkMissingParamsInAbsolutePath(matcher, parent);
|
1321 |
|
1322 |
|
1323 | if (originalRecord) {
|
1324 | originalRecord.alias.push(matcher);
|
1325 | {
|
1326 | checkSameParams(originalRecord, matcher);
|
1327 | }
|
1328 | }
|
1329 | else {
|
1330 |
|
1331 | originalMatcher = originalMatcher || matcher;
|
1332 | if (originalMatcher !== matcher)
|
1333 | originalMatcher.alias.push(matcher);
|
1334 |
|
1335 |
|
1336 | if (isRootAdd && record.name && !isAliasRecord(matcher))
|
1337 | removeRoute(record.name);
|
1338 | }
|
1339 | if ('children' in mainNormalizedRecord) {
|
1340 | const children = mainNormalizedRecord.children;
|
1341 | for (let i = 0; i < children.length; i++) {
|
1342 | addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
|
1343 | }
|
1344 | }
|
1345 |
|
1346 |
|
1347 | originalRecord = originalRecord || matcher;
|
1348 |
|
1349 |
|
1350 |
|
1351 |
|
1352 | insertMatcher(matcher);
|
1353 | }
|
1354 | return originalMatcher
|
1355 | ? () => {
|
1356 |
|
1357 | removeRoute(originalMatcher);
|
1358 | }
|
1359 | : noop;
|
1360 | }
|
1361 | function removeRoute(matcherRef) {
|
1362 | if (isRouteName(matcherRef)) {
|
1363 | const matcher = matcherMap.get(matcherRef);
|
1364 | if (matcher) {
|
1365 | matcherMap.delete(matcherRef);
|
1366 | matchers.splice(matchers.indexOf(matcher), 1);
|
1367 | matcher.children.forEach(removeRoute);
|
1368 | matcher.alias.forEach(removeRoute);
|
1369 | }
|
1370 | }
|
1371 | else {
|
1372 | const index = matchers.indexOf(matcherRef);
|
1373 | if (index > -1) {
|
1374 | matchers.splice(index, 1);
|
1375 | if (matcherRef.record.name)
|
1376 | matcherMap.delete(matcherRef.record.name);
|
1377 | matcherRef.children.forEach(removeRoute);
|
1378 | matcherRef.alias.forEach(removeRoute);
|
1379 | }
|
1380 | }
|
1381 | }
|
1382 | function getRoutes() {
|
1383 | return matchers;
|
1384 | }
|
1385 | function insertMatcher(matcher) {
|
1386 | let i = 0;
|
1387 | while (i < matchers.length &&
|
1388 | comparePathParserScore(matcher, matchers[i]) >= 0 &&
|
1389 |
|
1390 |
|
1391 | (matcher.record.path !== matchers[i].record.path ||
|
1392 | !isRecordChildOf(matcher, matchers[i])))
|
1393 | i++;
|
1394 | matchers.splice(i, 0, matcher);
|
1395 |
|
1396 | if (matcher.record.name && !isAliasRecord(matcher))
|
1397 | matcherMap.set(matcher.record.name, matcher);
|
1398 | }
|
1399 | function resolve(location, currentLocation) {
|
1400 | let matcher;
|
1401 | let params = {};
|
1402 | let path;
|
1403 | let name;
|
1404 | if ('name' in location && location.name) {
|
1405 | matcher = matcherMap.get(location.name);
|
1406 | if (!matcher)
|
1407 | throw createRouterError(1 , {
|
1408 | location,
|
1409 | });
|
1410 | name = matcher.record.name;
|
1411 | params = assign(
|
1412 |
|
1413 | paramsFromLocation(currentLocation.params,
|
1414 |
|
1415 |
|
1416 | matcher.keys.filter(k => !k.optional).map(k => k.name)), location.params);
|
1417 |
|
1418 | path = matcher.stringify(params);
|
1419 | }
|
1420 | else if ('path' in location) {
|
1421 |
|
1422 |
|
1423 | path = location.path;
|
1424 | if (!path.startsWith('/')) {
|
1425 | warn(`The Matcher cannot resolve relative paths but received "${path}". Unless you directly called \`matcher.resolve("${path}")\`, this is probably a bug in vue-router. Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/router.`);
|
1426 | }
|
1427 | matcher = matchers.find(m => m.re.test(path));
|
1428 |
|
1429 | if (matcher) {
|
1430 |
|
1431 |
|
1432 | params = matcher.parse(path);
|
1433 | name = matcher.record.name;
|
1434 | }
|
1435 |
|
1436 | }
|
1437 | else {
|
1438 |
|
1439 | matcher = currentLocation.name
|
1440 | ? matcherMap.get(currentLocation.name)
|
1441 | : matchers.find(m => m.re.test(currentLocation.path));
|
1442 | if (!matcher)
|
1443 | throw createRouterError(1 , {
|
1444 | location,
|
1445 | currentLocation,
|
1446 | });
|
1447 | name = matcher.record.name;
|
1448 |
|
1449 |
|
1450 | params = assign({}, currentLocation.params, location.params);
|
1451 | path = matcher.stringify(params);
|
1452 | }
|
1453 | const matched = [];
|
1454 | let parentMatcher = matcher;
|
1455 | while (parentMatcher) {
|
1456 |
|
1457 | matched.unshift(parentMatcher.record);
|
1458 | parentMatcher = parentMatcher.parent;
|
1459 | }
|
1460 | return {
|
1461 | name,
|
1462 | path,
|
1463 | params,
|
1464 | matched,
|
1465 | meta: mergeMetaFields(matched),
|
1466 | };
|
1467 | }
|
1468 |
|
1469 | routes.forEach(route => addRoute(route));
|
1470 | return { addRoute, resolve, removeRoute, getRoutes, getRecordMatcher };
|
1471 | }
|
1472 | function paramsFromLocation(params, keys) {
|
1473 | const newParams = {};
|
1474 | for (const key of keys) {
|
1475 | if (key in params)
|
1476 | newParams[key] = params[key];
|
1477 | }
|
1478 | return newParams;
|
1479 | }
|
1480 |
|
1481 |
|
1482 |
|
1483 |
|
1484 |
|
1485 |
|
1486 | function normalizeRouteRecord(record) {
|
1487 | return {
|
1488 | path: record.path,
|
1489 | redirect: record.redirect,
|
1490 | name: record.name,
|
1491 | meta: record.meta || {},
|
1492 | aliasOf: undefined,
|
1493 | beforeEnter: record.beforeEnter,
|
1494 | props: normalizeRecordProps(record),
|
1495 | children: record.children || [],
|
1496 | instances: {},
|
1497 | leaveGuards: new Set(),
|
1498 | updateGuards: new Set(),
|
1499 | enterCallbacks: {},
|
1500 | components: 'components' in record
|
1501 | ? record.components || {}
|
1502 | : { default: record.component },
|
1503 | };
|
1504 | }
|
1505 |
|
1506 |
|
1507 |
|
1508 |
|
1509 |
|
1510 | function normalizeRecordProps(record) {
|
1511 | const propsObject = {};
|
1512 |
|
1513 | const props = record.props || false;
|
1514 | if ('component' in record) {
|
1515 | propsObject.default = props;
|
1516 | }
|
1517 | else {
|
1518 |
|
1519 |
|
1520 | for (const name in record.components)
|
1521 | propsObject[name] = typeof props === 'boolean' ? props : props[name];
|
1522 | }
|
1523 | return propsObject;
|
1524 | }
|
1525 |
|
1526 |
|
1527 |
|
1528 |
|
1529 | function isAliasRecord(record) {
|
1530 | while (record) {
|
1531 | if (record.record.aliasOf)
|
1532 | return true;
|
1533 | record = record.parent;
|
1534 | }
|
1535 | return false;
|
1536 | }
|
1537 |
|
1538 |
|
1539 |
|
1540 |
|
1541 |
|
1542 | function mergeMetaFields(matched) {
|
1543 | return matched.reduce((meta, record) => assign(meta, record.meta), {});
|
1544 | }
|
1545 | function mergeOptions(defaults, partialOptions) {
|
1546 | const options = {};
|
1547 | for (const key in defaults) {
|
1548 | options[key] = key in partialOptions ? partialOptions[key] : defaults[key];
|
1549 | }
|
1550 | return options;
|
1551 | }
|
1552 | function isSameParam(a, b) {
|
1553 | return (a.name === b.name &&
|
1554 | a.optional === b.optional &&
|
1555 | a.repeatable === b.repeatable);
|
1556 | }
|
1557 |
|
1558 |
|
1559 |
|
1560 |
|
1561 |
|
1562 |
|
1563 | function checkSameParams(a, b) {
|
1564 | for (const key of a.keys) {
|
1565 | if (!key.optional && !b.keys.find(isSameParam.bind(null, key)))
|
1566 | return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
|
1567 | }
|
1568 | for (const key of b.keys) {
|
1569 | if (!key.optional && !a.keys.find(isSameParam.bind(null, key)))
|
1570 | return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" should have the exact same param named "${key.name}"`);
|
1571 | }
|
1572 | }
|
1573 | function checkMissingParamsInAbsolutePath(record, parent) {
|
1574 | for (const key of parent.keys) {
|
1575 | if (!record.keys.find(isSameParam.bind(null, key)))
|
1576 | return warn(`Absolute path "${record.record.path}" should have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
|
1577 | }
|
1578 | }
|
1579 | function isRecordChildOf(record, parent) {
|
1580 | return parent.children.some(child => child === record || isRecordChildOf(record, child));
|
1581 | }
|
1582 |
|
1583 |
|
1584 |
|
1585 |
|
1586 |
|
1587 |
|
1588 |
|
1589 |
|
1590 |
|
1591 |
|
1592 |
|
1593 |
|
1594 |
|
1595 |
|
1596 |
|
1597 |
|
1598 |
|
1599 |
|
1600 | const HASH_RE = /#/g;
|
1601 | const AMPERSAND_RE = /&/g;
|
1602 | const SLASH_RE = /\//g;
|
1603 | const EQUAL_RE = /=/g;
|
1604 | const IM_RE = /\?/g;
|
1605 | const PLUS_RE = /\+/g;
|
1606 |
|
1607 |
|
1608 |
|
1609 |
|
1610 |
|
1611 |
|
1612 |
|
1613 |
|
1614 |
|
1615 |
|
1616 |
|
1617 |
|
1618 |
|
1619 |
|
1620 | const ENC_BRACKET_OPEN_RE = /%5B/g;
|
1621 | const ENC_BRACKET_CLOSE_RE = /%5D/g;
|
1622 | const ENC_CARET_RE = /%5E/g;
|
1623 | const ENC_BACKTICK_RE = /%60/g;
|
1624 | const ENC_CURLY_OPEN_RE = /%7B/g;
|
1625 | const ENC_PIPE_RE = /%7C/g;
|
1626 | const ENC_CURLY_CLOSE_RE = /%7D/g;
|
1627 | const ENC_SPACE_RE = /%20/g;
|
1628 |
|
1629 |
|
1630 |
|
1631 |
|
1632 |
|
1633 |
|
1634 |
|
1635 |
|
1636 | function commonEncode(text) {
|
1637 | return encodeURI('' + text)
|
1638 | .replace(ENC_PIPE_RE, '|')
|
1639 | .replace(ENC_BRACKET_OPEN_RE, '[')
|
1640 | .replace(ENC_BRACKET_CLOSE_RE, ']');
|
1641 | }
|
1642 |
|
1643 |
|
1644 |
|
1645 |
|
1646 |
|
1647 |
|
1648 | function encodeHash(text) {
|
1649 | return commonEncode(text)
|
1650 | .replace(ENC_CURLY_OPEN_RE, '{')
|
1651 | .replace(ENC_CURLY_CLOSE_RE, '}')
|
1652 | .replace(ENC_CARET_RE, '^');
|
1653 | }
|
1654 |
|
1655 |
|
1656 |
|
1657 |
|
1658 |
|
1659 |
|
1660 |
|
1661 | function encodeQueryValue(text) {
|
1662 | return (commonEncode(text)
|
1663 |
|
1664 | .replace(PLUS_RE, '%2B')
|
1665 | .replace(ENC_SPACE_RE, '+')
|
1666 | .replace(HASH_RE, '%23')
|
1667 | .replace(AMPERSAND_RE, '%26')
|
1668 | .replace(ENC_BACKTICK_RE, '`')
|
1669 | .replace(ENC_CURLY_OPEN_RE, '{')
|
1670 | .replace(ENC_CURLY_CLOSE_RE, '}')
|
1671 | .replace(ENC_CARET_RE, '^'));
|
1672 | }
|
1673 |
|
1674 |
|
1675 |
|
1676 |
|
1677 |
|
1678 | function encodeQueryKey(text) {
|
1679 | return encodeQueryValue(text).replace(EQUAL_RE, '%3D');
|
1680 | }
|
1681 |
|
1682 |
|
1683 |
|
1684 |
|
1685 |
|
1686 |
|
1687 | function encodePath(text) {
|
1688 | return commonEncode(text).replace(HASH_RE, '%23').replace(IM_RE, '%3F');
|
1689 | }
|
1690 |
|
1691 |
|
1692 |
|
1693 |
|
1694 |
|
1695 |
|
1696 |
|
1697 |
|
1698 |
|
1699 | function encodeParam(text) {
|
1700 | return text == null ? '' : encodePath(text).replace(SLASH_RE, '%2F');
|
1701 | }
|
1702 |
|
1703 |
|
1704 |
|
1705 |
|
1706 |
|
1707 |
|
1708 |
|
1709 | function decode(text) {
|
1710 | try {
|
1711 | return decodeURIComponent('' + text);
|
1712 | }
|
1713 | catch (err) {
|
1714 | warn(`Error decoding "${text}". Using original value`);
|
1715 | }
|
1716 | return '' + text;
|
1717 | }
|
1718 |
|
1719 |
|
1720 |
|
1721 |
|
1722 |
|
1723 |
|
1724 |
|
1725 |
|
1726 |
|
1727 |
|
1728 | function parseQuery(search) {
|
1729 | const query = {};
|
1730 |
|
1731 |
|
1732 | if (search === '' || search === '?')
|
1733 | return query;
|
1734 | const hasLeadingIM = search[0] === '?';
|
1735 | const searchParams = (hasLeadingIM ? search.slice(1) : search).split('&');
|
1736 | for (let i = 0; i < searchParams.length; ++i) {
|
1737 |
|
1738 | const searchParam = searchParams[i].replace(PLUS_RE, ' ');
|
1739 |
|
1740 | const eqPos = searchParam.indexOf('=');
|
1741 | const key = decode(eqPos < 0 ? searchParam : searchParam.slice(0, eqPos));
|
1742 | const value = eqPos < 0 ? null : decode(searchParam.slice(eqPos + 1));
|
1743 | if (key in query) {
|
1744 |
|
1745 | let currentValue = query[key];
|
1746 | if (!Array.isArray(currentValue)) {
|
1747 | currentValue = query[key] = [currentValue];
|
1748 | }
|
1749 | currentValue.push(value);
|
1750 | }
|
1751 | else {
|
1752 | query[key] = value;
|
1753 | }
|
1754 | }
|
1755 | return query;
|
1756 | }
|
1757 |
|
1758 |
|
1759 |
|
1760 |
|
1761 |
|
1762 |
|
1763 |
|
1764 |
|
1765 |
|
1766 | function stringifyQuery(query) {
|
1767 | let search = '';
|
1768 | for (let key in query) {
|
1769 | const value = query[key];
|
1770 | key = encodeQueryKey(key);
|
1771 | if (value == null) {
|
1772 |
|
1773 | if (value !== undefined) {
|
1774 | search += (search.length ? '&' : '') + key;
|
1775 | }
|
1776 | continue;
|
1777 | }
|
1778 |
|
1779 | const values = Array.isArray(value)
|
1780 | ? value.map(v => v && encodeQueryValue(v))
|
1781 | : [value && encodeQueryValue(value)];
|
1782 | values.forEach(value => {
|
1783 |
|
1784 |
|
1785 | if (value !== undefined) {
|
1786 |
|
1787 | search += (search.length ? '&' : '') + key;
|
1788 | if (value != null)
|
1789 | search += '=' + value;
|
1790 | }
|
1791 | });
|
1792 | }
|
1793 | return search;
|
1794 | }
|
1795 |
|
1796 |
|
1797 |
|
1798 |
|
1799 |
|
1800 |
|
1801 |
|
1802 |
|
1803 | function normalizeQuery(query) {
|
1804 | const normalizedQuery = {};
|
1805 | for (const key in query) {
|
1806 | const value = query[key];
|
1807 | if (value !== undefined) {
|
1808 | normalizedQuery[key] = Array.isArray(value)
|
1809 | ? value.map(v => (v == null ? null : '' + v))
|
1810 | : value == null
|
1811 | ? value
|
1812 | : '' + value;
|
1813 | }
|
1814 | }
|
1815 | return normalizedQuery;
|
1816 | }
|
1817 |
|
1818 |
|
1819 |
|
1820 |
|
1821 | function useCallbacks() {
|
1822 | let handlers = [];
|
1823 | function add(handler) {
|
1824 | handlers.push(handler);
|
1825 | return () => {
|
1826 | const i = handlers.indexOf(handler);
|
1827 | if (i > -1)
|
1828 | handlers.splice(i, 1);
|
1829 | };
|
1830 | }
|
1831 | function reset() {
|
1832 | handlers = [];
|
1833 | }
|
1834 | return {
|
1835 | add,
|
1836 | list: () => handlers,
|
1837 | reset,
|
1838 | };
|
1839 | }
|
1840 |
|
1841 | function registerGuard(record, name, guard) {
|
1842 | const removeFromList = () => {
|
1843 | record[name].delete(guard);
|
1844 | };
|
1845 | vue.onUnmounted(removeFromList);
|
1846 | vue.onDeactivated(removeFromList);
|
1847 | vue.onActivated(() => {
|
1848 | record[name].add(guard);
|
1849 | });
|
1850 | record[name].add(guard);
|
1851 | }
|
1852 |
|
1853 |
|
1854 |
|
1855 |
|
1856 |
|
1857 |
|
1858 |
|
1859 | function onBeforeRouteLeave(leaveGuard) {
|
1860 | if (!vue.getCurrentInstance()) {
|
1861 | warn('getCurrentInstance() returned null. onBeforeRouteLeave() must be called at the top of a setup function');
|
1862 | return;
|
1863 | }
|
1864 | const activeRecord = vue.inject(matchedRouteKey,
|
1865 |
|
1866 | {}).value;
|
1867 | if (!activeRecord) {
|
1868 | warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?');
|
1869 | return;
|
1870 | }
|
1871 | registerGuard(activeRecord, 'leaveGuards', leaveGuard);
|
1872 | }
|
1873 |
|
1874 |
|
1875 |
|
1876 |
|
1877 |
|
1878 |
|
1879 |
|
1880 | function onBeforeRouteUpdate(updateGuard) {
|
1881 | if (!vue.getCurrentInstance()) {
|
1882 | warn('getCurrentInstance() returned null. onBeforeRouteUpdate() must be called at the top of a setup function');
|
1883 | return;
|
1884 | }
|
1885 | const activeRecord = vue.inject(matchedRouteKey,
|
1886 |
|
1887 | {}).value;
|
1888 | if (!activeRecord) {
|
1889 | warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside of a component child of <router-view>. Maybe you called it inside of App.vue?');
|
1890 | return;
|
1891 | }
|
1892 | registerGuard(activeRecord, 'updateGuards', updateGuard);
|
1893 | }
|
1894 | function guardToPromiseFn(guard, to, from, record, name) {
|
1895 |
|
1896 | const enterCallbackArray = record &&
|
1897 |
|
1898 | (record.enterCallbacks[name] = record.enterCallbacks[name] || []);
|
1899 | return () => new Promise((resolve, reject) => {
|
1900 | const next = (valid) => {
|
1901 | if (valid === false)
|
1902 | reject(createRouterError(4 , {
|
1903 | from,
|
1904 | to,
|
1905 | }));
|
1906 | else if (valid instanceof Error) {
|
1907 | reject(valid);
|
1908 | }
|
1909 | else if (isRouteLocation(valid)) {
|
1910 | reject(createRouterError(2 , {
|
1911 | from: to,
|
1912 | to: valid,
|
1913 | }));
|
1914 | }
|
1915 | else {
|
1916 | if (enterCallbackArray &&
|
1917 |
|
1918 | record.enterCallbacks[name] === enterCallbackArray &&
|
1919 | typeof valid === 'function')
|
1920 | enterCallbackArray.push(valid);
|
1921 | resolve();
|
1922 | }
|
1923 | };
|
1924 |
|
1925 | const guardReturn = guard.call(record && record.instances[name], to, from, canOnlyBeCalledOnce(next, to, from) );
|
1926 | let guardCall = Promise.resolve(guardReturn);
|
1927 | if (guard.length < 3)
|
1928 | guardCall = guardCall.then(next);
|
1929 | if (guard.length > 2) {
|
1930 | const message = `The "next" callback was never called inside of ${guard.name ? '"' + guard.name + '"' : ''}:\n${guard.toString()}\n. If you are returning a value instead of calling "next", make sure to remove the "next" parameter from your function.`;
|
1931 | if (typeof guardReturn === 'object' && 'then' in guardReturn) {
|
1932 | guardCall = guardCall.then(resolvedValue => {
|
1933 |
|
1934 | if (!next._called) {
|
1935 | warn(message);
|
1936 | return Promise.reject(new Error('Invalid navigation guard'));
|
1937 | }
|
1938 | return resolvedValue;
|
1939 | });
|
1940 |
|
1941 | }
|
1942 | else if (guardReturn !== undefined) {
|
1943 |
|
1944 | if (!next._called) {
|
1945 | warn(message);
|
1946 | reject(new Error('Invalid navigation guard'));
|
1947 | return;
|
1948 | }
|
1949 | }
|
1950 | }
|
1951 | guardCall.catch(err => reject(err));
|
1952 | });
|
1953 | }
|
1954 | function canOnlyBeCalledOnce(next, to, from) {
|
1955 | let called = 0;
|
1956 | return function () {
|
1957 | if (called++ === 1)
|
1958 | warn(`The "next" callback was called more than once in one navigation guard when going from "${from.fullPath}" to "${to.fullPath}". It should be called exactly one time in each navigation guard. This will fail in production.`);
|
1959 |
|
1960 | next._called = true;
|
1961 | if (called === 1)
|
1962 | next.apply(null, arguments);
|
1963 | };
|
1964 | }
|
1965 | function extractComponentsGuards(matched, guardType, to, from) {
|
1966 | const guards = [];
|
1967 | for (const record of matched) {
|
1968 | for (const name in record.components) {
|
1969 | let rawComponent = record.components[name];
|
1970 | {
|
1971 | if (!rawComponent ||
|
1972 | (typeof rawComponent !== 'object' &&
|
1973 | typeof rawComponent !== 'function')) {
|
1974 | warn(`Component "${name}" in record with path "${record.path}" is not` +
|
1975 | ` a valid component. Received "${String(rawComponent)}".`);
|
1976 |
|
1977 |
|
1978 | throw new Error('Invalid route component');
|
1979 | }
|
1980 | else if ('then' in rawComponent) {
|
1981 |
|
1982 |
|
1983 | warn(`Component "${name}" in record with path "${record.path}" is a ` +
|
1984 | `Promise instead of a function that returns a Promise. Did you ` +
|
1985 | `write "import('./MyPage.vue')" instead of ` +
|
1986 | `"() => import('./MyPage.vue')" ? This will break in ` +
|
1987 | `production if not fixed.`);
|
1988 | const promise = rawComponent;
|
1989 | rawComponent = () => promise;
|
1990 | }
|
1991 | else if (rawComponent.__asyncLoader &&
|
1992 |
|
1993 | !rawComponent.__warnedDefineAsync) {
|
1994 | rawComponent.__warnedDefineAsync = true;
|
1995 | warn(`Component "${name}" in record with path "${record.path}" is defined ` +
|
1996 | `using "defineAsyncComponent()". ` +
|
1997 | `Write "() => import('./MyPage.vue')" instead of ` +
|
1998 | `"defineAsyncComponent(() => import('./MyPage.vue'))".`);
|
1999 | }
|
2000 | }
|
2001 |
|
2002 | if (guardType !== 'beforeRouteEnter' && !record.instances[name])
|
2003 | continue;
|
2004 | if (isRouteComponent(rawComponent)) {
|
2005 |
|
2006 | const options = rawComponent.__vccOpts || rawComponent;
|
2007 | const guard = options[guardType];
|
2008 | guard && guards.push(guardToPromiseFn(guard, to, from, record, name));
|
2009 | }
|
2010 | else {
|
2011 |
|
2012 | let componentPromise = rawComponent();
|
2013 | if (!('catch' in componentPromise)) {
|
2014 | warn(`Component "${name}" in record with path "${record.path}" is a function that does not return a Promise. If you were passing a functional component, make sure to add a "displayName" to the component. This will break in production if not fixed.`);
|
2015 | componentPromise = Promise.resolve(componentPromise);
|
2016 | }
|
2017 | guards.push(() => componentPromise.then(resolved => {
|
2018 | if (!resolved)
|
2019 | return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}"`));
|
2020 | const resolvedComponent = isESModule(resolved)
|
2021 | ? resolved.default
|
2022 | : resolved;
|
2023 |
|
2024 | record.components[name] = resolvedComponent;
|
2025 |
|
2026 | const options = resolvedComponent.__vccOpts || resolvedComponent;
|
2027 | const guard = options[guardType];
|
2028 | return guard && guardToPromiseFn(guard, to, from, record, name)();
|
2029 | }));
|
2030 | }
|
2031 | }
|
2032 | }
|
2033 | return guards;
|
2034 | }
|
2035 |
|
2036 |
|
2037 |
|
2038 |
|
2039 |
|
2040 | function isRouteComponent(component) {
|
2041 | return (typeof component === 'object' ||
|
2042 | 'displayName' in component ||
|
2043 | 'props' in component ||
|
2044 | '__vccOpts' in component);
|
2045 | }
|
2046 |
|
2047 |
|
2048 |
|
2049 | function useLink(props) {
|
2050 | const router = vue.inject(routerKey);
|
2051 | const currentRoute = vue.inject(routeLocationKey);
|
2052 | const route = vue.computed(() => router.resolve(vue.unref(props.to)));
|
2053 | const activeRecordIndex = vue.computed(() => {
|
2054 | const { matched } = route.value;
|
2055 | const { length } = matched;
|
2056 | const routeMatched = matched[length - 1];
|
2057 | const currentMatched = currentRoute.matched;
|
2058 | if (!routeMatched || !currentMatched.length)
|
2059 | return -1;
|
2060 | const index = currentMatched.findIndex(isSameRouteRecord.bind(null, routeMatched));
|
2061 | if (index > -1)
|
2062 | return index;
|
2063 |
|
2064 | const parentRecordPath = getOriginalPath(matched[length - 2]);
|
2065 | return (
|
2066 |
|
2067 | length > 1 &&
|
2068 |
|
2069 |
|
2070 |
|
2071 | getOriginalPath(routeMatched) === parentRecordPath &&
|
2072 |
|
2073 | currentMatched[currentMatched.length - 1].path !== parentRecordPath
|
2074 | ? currentMatched.findIndex(isSameRouteRecord.bind(null, matched[length - 2]))
|
2075 | : index);
|
2076 | });
|
2077 | const isActive = vue.computed(() => activeRecordIndex.value > -1 &&
|
2078 | includesParams(currentRoute.params, route.value.params));
|
2079 | const isExactActive = vue.computed(() => activeRecordIndex.value > -1 &&
|
2080 | activeRecordIndex.value === currentRoute.matched.length - 1 &&
|
2081 | isSameRouteLocationParams(currentRoute.params, route.value.params));
|
2082 | function navigate(e = {}) {
|
2083 | if (guardEvent(e)) {
|
2084 | return router[vue.unref(props.replace) ? 'replace' : 'push'](vue.unref(props.to)
|
2085 |
|
2086 | ).catch(noop);
|
2087 | }
|
2088 | return Promise.resolve();
|
2089 | }
|
2090 |
|
2091 | if (isBrowser) {
|
2092 | const instance = vue.getCurrentInstance();
|
2093 | if (instance) {
|
2094 | const linkContextDevtools = {
|
2095 | route: route.value,
|
2096 | isActive: isActive.value,
|
2097 | isExactActive: isExactActive.value,
|
2098 | };
|
2099 |
|
2100 | instance.__vrl_devtools = instance.__vrl_devtools || [];
|
2101 |
|
2102 | instance.__vrl_devtools.push(linkContextDevtools);
|
2103 | vue.watchEffect(() => {
|
2104 | linkContextDevtools.route = route.value;
|
2105 | linkContextDevtools.isActive = isActive.value;
|
2106 | linkContextDevtools.isExactActive = isExactActive.value;
|
2107 | }, { flush: 'post' });
|
2108 | }
|
2109 | }
|
2110 | return {
|
2111 | route,
|
2112 | href: vue.computed(() => route.value.href),
|
2113 | isActive,
|
2114 | isExactActive,
|
2115 | navigate,
|
2116 | };
|
2117 | }
|
2118 | const RouterLinkImpl = vue.defineComponent({
|
2119 | name: 'RouterLink',
|
2120 | props: {
|
2121 | to: {
|
2122 | type: [String, Object],
|
2123 | required: true,
|
2124 | },
|
2125 | replace: Boolean,
|
2126 | activeClass: String,
|
2127 |
|
2128 | exactActiveClass: String,
|
2129 | custom: Boolean,
|
2130 | ariaCurrentValue: {
|
2131 | type: String,
|
2132 | default: 'page',
|
2133 | },
|
2134 | },
|
2135 | useLink,
|
2136 | setup(props, { slots }) {
|
2137 | const link = vue.reactive(useLink(props));
|
2138 | const { options } = vue.inject(routerKey);
|
2139 | const elClass = vue.computed(() => ({
|
2140 | [getLinkClass(props.activeClass, options.linkActiveClass, 'router-link-active')]: link.isActive,
|
2141 |
|
2142 |
|
2143 |
|
2144 |
|
2145 |
|
2146 | [getLinkClass(props.exactActiveClass, options.linkExactActiveClass, 'router-link-exact-active')]: link.isExactActive,
|
2147 | }));
|
2148 | return () => {
|
2149 | const children = slots.default && slots.default(link);
|
2150 | return props.custom
|
2151 | ? children
|
2152 | : vue.h('a', {
|
2153 | 'aria-current': link.isExactActive
|
2154 | ? props.ariaCurrentValue
|
2155 | : null,
|
2156 | href: link.href,
|
2157 |
|
2158 |
|
2159 | onClick: link.navigate,
|
2160 | class: elClass.value,
|
2161 | }, children);
|
2162 | };
|
2163 | },
|
2164 | });
|
2165 |
|
2166 |
|
2167 |
|
2168 |
|
2169 |
|
2170 | const RouterLink = RouterLinkImpl;
|
2171 | function guardEvent(e) {
|
2172 |
|
2173 | if (e.metaKey || e.altKey || e.ctrlKey || e.shiftKey)
|
2174 | return;
|
2175 |
|
2176 | if (e.defaultPrevented)
|
2177 | return;
|
2178 |
|
2179 | if (e.button !== undefined && e.button !== 0)
|
2180 | return;
|
2181 |
|
2182 |
|
2183 | if (e.currentTarget && e.currentTarget.getAttribute) {
|
2184 |
|
2185 | const target = e.currentTarget.getAttribute('target');
|
2186 | if (/\b_blank\b/i.test(target))
|
2187 | return;
|
2188 | }
|
2189 |
|
2190 | if (e.preventDefault)
|
2191 | e.preventDefault();
|
2192 | return true;
|
2193 | }
|
2194 | function includesParams(outer, inner) {
|
2195 | for (const key in inner) {
|
2196 | const innerValue = inner[key];
|
2197 | const outerValue = outer[key];
|
2198 | if (typeof innerValue === 'string') {
|
2199 | if (innerValue !== outerValue)
|
2200 | return false;
|
2201 | }
|
2202 | else {
|
2203 | if (!Array.isArray(outerValue) ||
|
2204 | outerValue.length !== innerValue.length ||
|
2205 | innerValue.some((value, i) => value !== outerValue[i]))
|
2206 | return false;
|
2207 | }
|
2208 | }
|
2209 | return true;
|
2210 | }
|
2211 |
|
2212 |
|
2213 |
|
2214 |
|
2215 | function getOriginalPath(record) {
|
2216 | return record ? (record.aliasOf ? record.aliasOf.path : record.path) : '';
|
2217 | }
|
2218 |
|
2219 |
|
2220 |
|
2221 |
|
2222 |
|
2223 |
|
2224 | const getLinkClass = (propClass, globalClass, defaultClass) => propClass != null
|
2225 | ? propClass
|
2226 | : globalClass != null
|
2227 | ? globalClass
|
2228 | : defaultClass;
|
2229 |
|
2230 | const RouterViewImpl = vue.defineComponent({
|
2231 | name: 'RouterView',
|
2232 |
|
2233 | inheritAttrs: false,
|
2234 | props: {
|
2235 | name: {
|
2236 | type: String,
|
2237 | default: 'default',
|
2238 | },
|
2239 | route: Object,
|
2240 | },
|
2241 |
|
2242 |
|
2243 | compatConfig: { MODE: 3 },
|
2244 | setup(props, { attrs, slots }) {
|
2245 | warnDeprecatedUsage();
|
2246 | const injectedRoute = vue.inject(routerViewLocationKey);
|
2247 | const routeToDisplay = vue.computed(() => props.route || injectedRoute.value);
|
2248 | const depth = vue.inject(viewDepthKey, 0);
|
2249 | const matchedRouteRef = vue.computed(() => routeToDisplay.value.matched[depth]);
|
2250 | vue.provide(viewDepthKey, depth + 1);
|
2251 | vue.provide(matchedRouteKey, matchedRouteRef);
|
2252 | vue.provide(routerViewLocationKey, routeToDisplay);
|
2253 | const viewRef = vue.ref();
|
2254 |
|
2255 |
|
2256 | vue.watch(() => [viewRef.value, matchedRouteRef.value, props.name], ([instance, to, name], [oldInstance, from, oldName]) => {
|
2257 |
|
2258 | if (to) {
|
2259 |
|
2260 |
|
2261 | to.instances[name] = instance;
|
2262 |
|
2263 |
|
2264 |
|
2265 |
|
2266 |
|
2267 |
|
2268 | if (from && from !== to && instance && instance === oldInstance) {
|
2269 | if (!to.leaveGuards.size) {
|
2270 | to.leaveGuards = from.leaveGuards;
|
2271 | }
|
2272 | if (!to.updateGuards.size) {
|
2273 | to.updateGuards = from.updateGuards;
|
2274 | }
|
2275 | }
|
2276 | }
|
2277 |
|
2278 | if (instance &&
|
2279 | to &&
|
2280 |
|
2281 |
|
2282 | (!from || !isSameRouteRecord(to, from) || !oldInstance)) {
|
2283 | (to.enterCallbacks[name] || []).forEach(callback => callback(instance));
|
2284 | }
|
2285 | }, { flush: 'post' });
|
2286 | return () => {
|
2287 | const route = routeToDisplay.value;
|
2288 | const matchedRoute = matchedRouteRef.value;
|
2289 | const ViewComponent = matchedRoute && matchedRoute.components[props.name];
|
2290 |
|
2291 |
|
2292 | const currentName = props.name;
|
2293 | if (!ViewComponent) {
|
2294 | return normalizeSlot(slots.default, { Component: ViewComponent, route });
|
2295 | }
|
2296 |
|
2297 | const routePropsOption = matchedRoute.props[props.name];
|
2298 | const routeProps = routePropsOption
|
2299 | ? routePropsOption === true
|
2300 | ? route.params
|
2301 | : typeof routePropsOption === 'function'
|
2302 | ? routePropsOption(route)
|
2303 | : routePropsOption
|
2304 | : null;
|
2305 | const onVnodeUnmounted = vnode => {
|
2306 |
|
2307 | if (vnode.component.isUnmounted) {
|
2308 | matchedRoute.instances[currentName] = null;
|
2309 | }
|
2310 | };
|
2311 | const component = vue.h(ViewComponent, assign({}, routeProps, attrs, {
|
2312 | onVnodeUnmounted,
|
2313 | ref: viewRef,
|
2314 | }));
|
2315 | if (isBrowser &&
|
2316 | component.ref) {
|
2317 |
|
2318 | const info = {
|
2319 | depth,
|
2320 | name: matchedRoute.name,
|
2321 | path: matchedRoute.path,
|
2322 | meta: matchedRoute.meta,
|
2323 | };
|
2324 | const internalInstances = Array.isArray(component.ref)
|
2325 | ? component.ref.map(r => r.i)
|
2326 | : [component.ref.i];
|
2327 | internalInstances.forEach(instance => {
|
2328 |
|
2329 | instance.__vrv_devtools = info;
|
2330 | });
|
2331 | }
|
2332 | return (
|
2333 |
|
2334 |
|
2335 | normalizeSlot(slots.default, { Component: component, route }) ||
|
2336 | component);
|
2337 | };
|
2338 | },
|
2339 | });
|
2340 | function normalizeSlot(slot, data) {
|
2341 | if (!slot)
|
2342 | return null;
|
2343 | const slotContent = slot(data);
|
2344 | return slotContent.length === 1 ? slotContent[0] : slotContent;
|
2345 | }
|
2346 |
|
2347 |
|
2348 |
|
2349 |
|
2350 |
|
2351 | const RouterView = RouterViewImpl;
|
2352 |
|
2353 |
|
2354 | function warnDeprecatedUsage() {
|
2355 | const instance = vue.getCurrentInstance();
|
2356 | const parentName = instance.parent && instance.parent.type.name;
|
2357 | if (parentName &&
|
2358 | (parentName === 'KeepAlive' || parentName.includes('Transition'))) {
|
2359 | const comp = parentName === 'KeepAlive' ? 'keep-alive' : 'transition';
|
2360 | warn(`<router-view> can no longer be used directly inside <transition> or <keep-alive>.\n` +
|
2361 | `Use slot props instead:\n\n` +
|
2362 | `<router-view v-slot="{ Component }">\n` +
|
2363 | ` <${comp}>\n` +
|
2364 | ` <component :is="Component" />\n` +
|
2365 | ` </${comp}>\n` +
|
2366 | `</router-view>`);
|
2367 | }
|
2368 | }
|
2369 |
|
2370 | function formatRouteLocation(routeLocation, tooltip) {
|
2371 | const copy = assign({}, routeLocation, {
|
2372 |
|
2373 | matched: routeLocation.matched.map(matched => omit(matched, ['instances', 'children', 'aliasOf'])),
|
2374 | });
|
2375 | return {
|
2376 | _custom: {
|
2377 | type: null,
|
2378 | readOnly: true,
|
2379 | display: routeLocation.fullPath,
|
2380 | tooltip,
|
2381 | value: copy,
|
2382 | },
|
2383 | };
|
2384 | }
|
2385 | function formatDisplay(display) {
|
2386 | return {
|
2387 | _custom: {
|
2388 | display,
|
2389 | },
|
2390 | };
|
2391 | }
|
2392 |
|
2393 | let routerId = 0;
|
2394 | function addDevtools(app, router, matcher) {
|
2395 |
|
2396 |
|
2397 | if (router.__hasDevtools)
|
2398 | return;
|
2399 | router.__hasDevtools = true;
|
2400 |
|
2401 | const id = routerId++;
|
2402 | devtoolsApi.setupDevtoolsPlugin({
|
2403 | id: 'org.vuejs.router' + (id ? '.' + id : ''),
|
2404 | label: 'Vue Router',
|
2405 | packageName: 'vue-router',
|
2406 | homepage: 'https://router.vuejs.org',
|
2407 | logo: 'https://router.vuejs.org/logo.png',
|
2408 | componentStateTypes: ['Routing'],
|
2409 | app,
|
2410 | }, api => {
|
2411 |
|
2412 | api.on.inspectComponent((payload, ctx) => {
|
2413 | if (payload.instanceData) {
|
2414 | payload.instanceData.state.push({
|
2415 | type: 'Routing',
|
2416 | key: '$route',
|
2417 | editable: false,
|
2418 | value: formatRouteLocation(router.currentRoute.value, 'Current Route'),
|
2419 | });
|
2420 | }
|
2421 | });
|
2422 |
|
2423 | api.on.visitComponentTree(({ treeNode: node, componentInstance }) => {
|
2424 | if (componentInstance.__vrv_devtools) {
|
2425 | const info = componentInstance.__vrv_devtools;
|
2426 | node.tags.push({
|
2427 | label: (info.name ? `${info.name.toString()}: ` : '') + info.path,
|
2428 | textColor: 0,
|
2429 | tooltip: 'This component is rendered by <router-view>',
|
2430 | backgroundColor: PINK_500,
|
2431 | });
|
2432 | }
|
2433 |
|
2434 | if (Array.isArray(componentInstance.__vrl_devtools)) {
|
2435 | componentInstance.__devtoolsApi = api;
|
2436 | componentInstance.__vrl_devtools.forEach(devtoolsData => {
|
2437 | let backgroundColor = ORANGE_400;
|
2438 | let tooltip = '';
|
2439 | if (devtoolsData.isExactActive) {
|
2440 | backgroundColor = LIME_500;
|
2441 | tooltip = 'This is exactly active';
|
2442 | }
|
2443 | else if (devtoolsData.isActive) {
|
2444 | backgroundColor = BLUE_600;
|
2445 | tooltip = 'This link is active';
|
2446 | }
|
2447 | node.tags.push({
|
2448 | label: devtoolsData.route.path,
|
2449 | textColor: 0,
|
2450 | tooltip,
|
2451 | backgroundColor,
|
2452 | });
|
2453 | });
|
2454 | }
|
2455 | });
|
2456 | vue.watch(router.currentRoute, () => {
|
2457 |
|
2458 | refreshRoutesView();
|
2459 | api.notifyComponentUpdate();
|
2460 | api.sendInspectorTree(routerInspectorId);
|
2461 | api.sendInspectorState(routerInspectorId);
|
2462 | });
|
2463 | const navigationsLayerId = 'router:navigations:' + id;
|
2464 | api.addTimelineLayer({
|
2465 | id: navigationsLayerId,
|
2466 | label: `Router${id ? ' ' + id : ''} Navigations`,
|
2467 | color: 0x40a8c4,
|
2468 | });
|
2469 |
|
2470 |
|
2471 |
|
2472 |
|
2473 |
|
2474 |
|
2475 | router.onError((error, to) => {
|
2476 | api.addTimelineEvent({
|
2477 | layerId: navigationsLayerId,
|
2478 | event: {
|
2479 | title: 'Error during Navigation',
|
2480 | subtitle: to.fullPath,
|
2481 | logType: 'error',
|
2482 | time: api.now(),
|
2483 | data: { error },
|
2484 | groupId: to.meta.__navigationId,
|
2485 | },
|
2486 | });
|
2487 | });
|
2488 |
|
2489 | let navigationId = 0;
|
2490 | router.beforeEach((to, from) => {
|
2491 | const data = {
|
2492 | guard: formatDisplay('beforeEach'),
|
2493 | from: formatRouteLocation(from, 'Current Location during this navigation'),
|
2494 | to: formatRouteLocation(to, 'Target location'),
|
2495 | };
|
2496 |
|
2497 | Object.defineProperty(to.meta, '__navigationId', {
|
2498 | value: navigationId++,
|
2499 | });
|
2500 | api.addTimelineEvent({
|
2501 | layerId: navigationsLayerId,
|
2502 | event: {
|
2503 | time: api.now(),
|
2504 | title: 'Start of navigation',
|
2505 | subtitle: to.fullPath,
|
2506 | data,
|
2507 | groupId: to.meta.__navigationId,
|
2508 | },
|
2509 | });
|
2510 | });
|
2511 | router.afterEach((to, from, failure) => {
|
2512 | const data = {
|
2513 | guard: formatDisplay('afterEach'),
|
2514 | };
|
2515 | if (failure) {
|
2516 | data.failure = {
|
2517 | _custom: {
|
2518 | type: Error,
|
2519 | readOnly: true,
|
2520 | display: failure ? failure.message : '',
|
2521 | tooltip: 'Navigation Failure',
|
2522 | value: failure,
|
2523 | },
|
2524 | };
|
2525 | data.status = formatDisplay('❌');
|
2526 | }
|
2527 | else {
|
2528 | data.status = formatDisplay('✅');
|
2529 | }
|
2530 |
|
2531 | data.from = formatRouteLocation(from, 'Current Location during this navigation');
|
2532 | data.to = formatRouteLocation(to, 'Target location');
|
2533 | api.addTimelineEvent({
|
2534 | layerId: navigationsLayerId,
|
2535 | event: {
|
2536 | title: 'End of navigation',
|
2537 | subtitle: to.fullPath,
|
2538 | time: api.now(),
|
2539 | data,
|
2540 | logType: failure ? 'warning' : 'default',
|
2541 | groupId: to.meta.__navigationId,
|
2542 | },
|
2543 | });
|
2544 | });
|
2545 | |
2546 |
|
2547 |
|
2548 | const routerInspectorId = 'router-inspector:' + id;
|
2549 | api.addInspector({
|
2550 | id: routerInspectorId,
|
2551 | label: 'Routes' + (id ? ' ' + id : ''),
|
2552 | icon: 'book',
|
2553 | treeFilterPlaceholder: 'Search routes',
|
2554 | });
|
2555 | function refreshRoutesView() {
|
2556 |
|
2557 | if (!activeRoutesPayload)
|
2558 | return;
|
2559 | const payload = activeRoutesPayload;
|
2560 |
|
2561 | let routes = matcher.getRoutes().filter(route => !route.parent);
|
2562 |
|
2563 | routes.forEach(resetMatchStateOnRouteRecord);
|
2564 |
|
2565 | if (payload.filter) {
|
2566 | routes = routes.filter(route =>
|
2567 |
|
2568 | isRouteMatching(route, payload.filter.toLowerCase()));
|
2569 | }
|
2570 |
|
2571 | routes.forEach(route => markRouteRecordActive(route, router.currentRoute.value));
|
2572 | payload.rootNodes = routes.map(formatRouteRecordForInspector);
|
2573 | }
|
2574 | let activeRoutesPayload;
|
2575 | api.on.getInspectorTree(payload => {
|
2576 | activeRoutesPayload = payload;
|
2577 | if (payload.app === app && payload.inspectorId === routerInspectorId) {
|
2578 | refreshRoutesView();
|
2579 | }
|
2580 | });
|
2581 | |
2582 |
|
2583 |
|
2584 | api.on.getInspectorState(payload => {
|
2585 | if (payload.app === app && payload.inspectorId === routerInspectorId) {
|
2586 | const routes = matcher.getRoutes();
|
2587 | const route = routes.find(route => route.record.__vd_id === payload.nodeId);
|
2588 | if (route) {
|
2589 | payload.state = {
|
2590 | options: formatRouteRecordMatcherForStateInspector(route),
|
2591 | };
|
2592 | }
|
2593 | }
|
2594 | });
|
2595 | api.sendInspectorTree(routerInspectorId);
|
2596 | api.sendInspectorState(routerInspectorId);
|
2597 | });
|
2598 | }
|
2599 | function modifierForKey(key) {
|
2600 | if (key.optional) {
|
2601 | return key.repeatable ? '*' : '?';
|
2602 | }
|
2603 | else {
|
2604 | return key.repeatable ? '+' : '';
|
2605 | }
|
2606 | }
|
2607 | function formatRouteRecordMatcherForStateInspector(route) {
|
2608 | const { record } = route;
|
2609 | const fields = [
|
2610 | { editable: false, key: 'path', value: record.path },
|
2611 | ];
|
2612 | if (record.name != null) {
|
2613 | fields.push({
|
2614 | editable: false,
|
2615 | key: 'name',
|
2616 | value: record.name,
|
2617 | });
|
2618 | }
|
2619 | fields.push({ editable: false, key: 'regexp', value: route.re });
|
2620 | if (route.keys.length) {
|
2621 | fields.push({
|
2622 | editable: false,
|
2623 | key: 'keys',
|
2624 | value: {
|
2625 | _custom: {
|
2626 | type: null,
|
2627 | readOnly: true,
|
2628 | display: route.keys
|
2629 | .map(key => `${key.name}${modifierForKey(key)}`)
|
2630 | .join(' '),
|
2631 | tooltip: 'Param keys',
|
2632 | value: route.keys,
|
2633 | },
|
2634 | },
|
2635 | });
|
2636 | }
|
2637 | if (record.redirect != null) {
|
2638 | fields.push({
|
2639 | editable: false,
|
2640 | key: 'redirect',
|
2641 | value: record.redirect,
|
2642 | });
|
2643 | }
|
2644 | if (route.alias.length) {
|
2645 | fields.push({
|
2646 | editable: false,
|
2647 | key: 'aliases',
|
2648 | value: route.alias.map(alias => alias.record.path),
|
2649 | });
|
2650 | }
|
2651 | fields.push({
|
2652 | key: 'score',
|
2653 | editable: false,
|
2654 | value: {
|
2655 | _custom: {
|
2656 | type: null,
|
2657 | readOnly: true,
|
2658 | display: route.score.map(score => score.join(', ')).join(' | '),
|
2659 | tooltip: 'Score used to sort routes',
|
2660 | value: route.score,
|
2661 | },
|
2662 | },
|
2663 | });
|
2664 | return fields;
|
2665 | }
|
2666 |
|
2667 |
|
2668 |
|
2669 | const PINK_500 = 0xec4899;
|
2670 | const BLUE_600 = 0x2563eb;
|
2671 | const LIME_500 = 0x84cc16;
|
2672 | const CYAN_400 = 0x22d3ee;
|
2673 | const ORANGE_400 = 0xfb923c;
|
2674 |
|
2675 | const DARK = 0x666666;
|
2676 | function formatRouteRecordForInspector(route) {
|
2677 | const tags = [];
|
2678 | const { record } = route;
|
2679 | if (record.name != null) {
|
2680 | tags.push({
|
2681 | label: String(record.name),
|
2682 | textColor: 0,
|
2683 | backgroundColor: CYAN_400,
|
2684 | });
|
2685 | }
|
2686 | if (record.aliasOf) {
|
2687 | tags.push({
|
2688 | label: 'alias',
|
2689 | textColor: 0,
|
2690 | backgroundColor: ORANGE_400,
|
2691 | });
|
2692 | }
|
2693 | if (route.__vd_match) {
|
2694 | tags.push({
|
2695 | label: 'matches',
|
2696 | textColor: 0,
|
2697 | backgroundColor: PINK_500,
|
2698 | });
|
2699 | }
|
2700 | if (route.__vd_exactActive) {
|
2701 | tags.push({
|
2702 | label: 'exact',
|
2703 | textColor: 0,
|
2704 | backgroundColor: LIME_500,
|
2705 | });
|
2706 | }
|
2707 | if (route.__vd_active) {
|
2708 | tags.push({
|
2709 | label: 'active',
|
2710 | textColor: 0,
|
2711 | backgroundColor: BLUE_600,
|
2712 | });
|
2713 | }
|
2714 | if (record.redirect) {
|
2715 | tags.push({
|
2716 | label: 'redirect: ' +
|
2717 | (typeof record.redirect === 'string' ? record.redirect : 'Object'),
|
2718 | textColor: 0xffffff,
|
2719 | backgroundColor: DARK,
|
2720 | });
|
2721 | }
|
2722 |
|
2723 |
|
2724 | let id = record.__vd_id;
|
2725 | if (id == null) {
|
2726 | id = String(routeRecordId++);
|
2727 | record.__vd_id = id;
|
2728 | }
|
2729 | return {
|
2730 | id,
|
2731 | label: record.path,
|
2732 | tags,
|
2733 | children: route.children.map(formatRouteRecordForInspector),
|
2734 | };
|
2735 | }
|
2736 |
|
2737 | let routeRecordId = 0;
|
2738 | const EXTRACT_REGEXP_RE = /^\/(.*)\/([a-z]*)$/;
|
2739 | function markRouteRecordActive(route, currentRoute) {
|
2740 |
|
2741 |
|
2742 | const isExactActive = currentRoute.matched.length &&
|
2743 | isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
|
2744 | route.__vd_exactActive = route.__vd_active = isExactActive;
|
2745 | if (!isExactActive) {
|
2746 | route.__vd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record));
|
2747 | }
|
2748 | route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute));
|
2749 | }
|
2750 | function resetMatchStateOnRouteRecord(route) {
|
2751 | route.__vd_match = false;
|
2752 | route.children.forEach(resetMatchStateOnRouteRecord);
|
2753 | }
|
2754 | function isRouteMatching(route, filter) {
|
2755 | const found = String(route.re).match(EXTRACT_REGEXP_RE);
|
2756 | route.__vd_match = false;
|
2757 | if (!found || found.length < 3) {
|
2758 | return false;
|
2759 | }
|
2760 |
|
2761 | const nonEndingRE = new RegExp(found[1].replace(/\$$/, ''), found[2]);
|
2762 | if (nonEndingRE.test(filter)) {
|
2763 |
|
2764 | route.children.forEach(child => isRouteMatching(child, filter));
|
2765 |
|
2766 | if (route.record.path !== '/' || filter === '/') {
|
2767 | route.__vd_match = route.re.test(filter);
|
2768 | return true;
|
2769 | }
|
2770 |
|
2771 | return false;
|
2772 | }
|
2773 | const path = route.record.path.toLowerCase();
|
2774 | const decodedPath = decode(path);
|
2775 |
|
2776 | if (!filter.startsWith('/') &&
|
2777 | (decodedPath.includes(filter) || path.includes(filter)))
|
2778 | return true;
|
2779 | if (decodedPath.startsWith(filter) || path.startsWith(filter))
|
2780 | return true;
|
2781 | if (route.record.name && String(route.record.name).includes(filter))
|
2782 | return true;
|
2783 | return route.children.some(child => isRouteMatching(child, filter));
|
2784 | }
|
2785 | function omit(obj, keys) {
|
2786 | const ret = {};
|
2787 | for (const key in obj) {
|
2788 | if (!keys.includes(key)) {
|
2789 |
|
2790 | ret[key] = obj[key];
|
2791 | }
|
2792 | }
|
2793 | return ret;
|
2794 | }
|
2795 |
|
2796 |
|
2797 |
|
2798 |
|
2799 |
|
2800 |
|
2801 | function createRouter(options) {
|
2802 | const matcher = createRouterMatcher(options.routes, options);
|
2803 | const parseQuery$1 = options.parseQuery || parseQuery;
|
2804 | const stringifyQuery$1 = options.stringifyQuery || stringifyQuery;
|
2805 | const routerHistory = options.history;
|
2806 | if (!routerHistory)
|
2807 | throw new Error('Provide the "history" option when calling "createRouter()":' +
|
2808 | ' https://next.router.vuejs.org/api/#history.');
|
2809 | const beforeGuards = useCallbacks();
|
2810 | const beforeResolveGuards = useCallbacks();
|
2811 | const afterGuards = useCallbacks();
|
2812 | const currentRoute = vue.shallowRef(START_LOCATION_NORMALIZED);
|
2813 | let pendingLocation = START_LOCATION_NORMALIZED;
|
2814 |
|
2815 | if (isBrowser && options.scrollBehavior && 'scrollRestoration' in history) {
|
2816 | history.scrollRestoration = 'manual';
|
2817 | }
|
2818 | const normalizeParams = applyToParams.bind(null, paramValue => '' + paramValue);
|
2819 | const encodeParams = applyToParams.bind(null, encodeParam);
|
2820 | const decodeParams =
|
2821 |
|
2822 | applyToParams.bind(null, decode);
|
2823 | function addRoute(parentOrRoute, route) {
|
2824 | let parent;
|
2825 | let record;
|
2826 | if (isRouteName(parentOrRoute)) {
|
2827 | parent = matcher.getRecordMatcher(parentOrRoute);
|
2828 | record = route;
|
2829 | }
|
2830 | else {
|
2831 | record = parentOrRoute;
|
2832 | }
|
2833 | return matcher.addRoute(record, parent);
|
2834 | }
|
2835 | function removeRoute(name) {
|
2836 | const recordMatcher = matcher.getRecordMatcher(name);
|
2837 | if (recordMatcher) {
|
2838 | matcher.removeRoute(recordMatcher);
|
2839 | }
|
2840 | else {
|
2841 | warn(`Cannot remove non-existent route "${String(name)}"`);
|
2842 | }
|
2843 | }
|
2844 | function getRoutes() {
|
2845 | return matcher.getRoutes().map(routeMatcher => routeMatcher.record);
|
2846 | }
|
2847 | function hasRoute(name) {
|
2848 | return !!matcher.getRecordMatcher(name);
|
2849 | }
|
2850 | function resolve(rawLocation, currentLocation) {
|
2851 |
|
2852 |
|
2853 | currentLocation = assign({}, currentLocation || currentRoute.value);
|
2854 | if (typeof rawLocation === 'string') {
|
2855 | const locationNormalized = parseURL(parseQuery$1, rawLocation, currentLocation.path);
|
2856 | const matchedRoute = matcher.resolve({ path: locationNormalized.path }, currentLocation);
|
2857 | const href = routerHistory.createHref(locationNormalized.fullPath);
|
2858 | {
|
2859 | if (href.startsWith('//'))
|
2860 | warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
|
2861 | else if (!matchedRoute.matched.length) {
|
2862 | warn(`No match found for location with path "${rawLocation}"`);
|
2863 | }
|
2864 | }
|
2865 |
|
2866 | return assign(locationNormalized, matchedRoute, {
|
2867 | params: decodeParams(matchedRoute.params),
|
2868 | hash: decode(locationNormalized.hash),
|
2869 | redirectedFrom: undefined,
|
2870 | href,
|
2871 | });
|
2872 | }
|
2873 | let matcherLocation;
|
2874 |
|
2875 | if ('path' in rawLocation) {
|
2876 | if ('params' in rawLocation &&
|
2877 | !('name' in rawLocation) &&
|
2878 |
|
2879 | Object.keys(rawLocation.params).length) {
|
2880 | warn(`Path "${
|
2881 | // @ts-expect-error: the type is never
|
2882 | rawLocation.path}" was passed with params but they will be ignored. Use a named route alongside params instead.`);
|
2883 | }
|
2884 | matcherLocation = assign({}, rawLocation, {
|
2885 | path: parseURL(parseQuery$1, rawLocation.path, currentLocation.path).path,
|
2886 | });
|
2887 | }
|
2888 | else {
|
2889 |
|
2890 | const targetParams = assign({}, rawLocation.params);
|
2891 | for (const key in targetParams) {
|
2892 | if (targetParams[key] == null) {
|
2893 | delete targetParams[key];
|
2894 | }
|
2895 | }
|
2896 |
|
2897 | matcherLocation = assign({}, rawLocation, {
|
2898 | params: encodeParams(rawLocation.params),
|
2899 | });
|
2900 |
|
2901 |
|
2902 | currentLocation.params = encodeParams(currentLocation.params);
|
2903 | }
|
2904 | const matchedRoute = matcher.resolve(matcherLocation, currentLocation);
|
2905 | const hash = rawLocation.hash || '';
|
2906 | if (hash && !hash.startsWith('#')) {
|
2907 | warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
|
2908 | }
|
2909 |
|
2910 |
|
2911 | matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
|
2912 | const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
|
2913 | hash: encodeHash(hash),
|
2914 | path: matchedRoute.path,
|
2915 | }));
|
2916 | const href = routerHistory.createHref(fullPath);
|
2917 | {
|
2918 | if (href.startsWith('//')) {
|
2919 | warn(`Location "${rawLocation}" resolved to "${href}". A resolved location cannot start with multiple slashes.`);
|
2920 | }
|
2921 | else if (!matchedRoute.matched.length) {
|
2922 | warn(`No match found for location with path "${'path' in rawLocation ? rawLocation.path : rawLocation}"`);
|
2923 | }
|
2924 | }
|
2925 | return assign({
|
2926 | fullPath,
|
2927 |
|
2928 |
|
2929 | hash,
|
2930 | query:
|
2931 |
|
2932 |
|
2933 |
|
2934 |
|
2935 |
|
2936 | stringifyQuery$1 === stringifyQuery
|
2937 | ? normalizeQuery(rawLocation.query)
|
2938 | : (rawLocation.query || {}),
|
2939 | }, matchedRoute, {
|
2940 | redirectedFrom: undefined,
|
2941 | href,
|
2942 | });
|
2943 | }
|
2944 | function locationAsObject(to) {
|
2945 | return typeof to === 'string'
|
2946 | ? parseURL(parseQuery$1, to, currentRoute.value.path)
|
2947 | : assign({}, to);
|
2948 | }
|
2949 | function checkCanceledNavigation(to, from) {
|
2950 | if (pendingLocation !== to) {
|
2951 | return createRouterError(8 , {
|
2952 | from,
|
2953 | to,
|
2954 | });
|
2955 | }
|
2956 | }
|
2957 | function push(to) {
|
2958 | return pushWithRedirect(to);
|
2959 | }
|
2960 | function replace(to) {
|
2961 | return push(assign(locationAsObject(to), { replace: true }));
|
2962 | }
|
2963 | function handleRedirectRecord(to) {
|
2964 | const lastMatched = to.matched[to.matched.length - 1];
|
2965 | if (lastMatched && lastMatched.redirect) {
|
2966 | const { redirect } = lastMatched;
|
2967 | let newTargetLocation = typeof redirect === 'function' ? redirect(to) : redirect;
|
2968 | if (typeof newTargetLocation === 'string') {
|
2969 | newTargetLocation =
|
2970 | newTargetLocation.includes('?') || newTargetLocation.includes('#')
|
2971 | ? (newTargetLocation = locationAsObject(newTargetLocation))
|
2972 | :
|
2973 | { path: newTargetLocation };
|
2974 |
|
2975 |
|
2976 | newTargetLocation.params = {};
|
2977 | }
|
2978 | if (!('path' in newTargetLocation) &&
|
2979 | !('name' in newTargetLocation)) {
|
2980 | warn(`Invalid redirect found:\n${JSON.stringify(newTargetLocation, null, 2)}\n when navigating to "${to.fullPath}". A redirect must contain a name or path. This will break in production.`);
|
2981 | throw new Error('Invalid redirect');
|
2982 | }
|
2983 | return assign({
|
2984 | query: to.query,
|
2985 | hash: to.hash,
|
2986 | params: to.params,
|
2987 | }, newTargetLocation);
|
2988 | }
|
2989 | }
|
2990 | function pushWithRedirect(to, redirectedFrom) {
|
2991 | const targetLocation = (pendingLocation = resolve(to));
|
2992 | const from = currentRoute.value;
|
2993 | const data = to.state;
|
2994 | const force = to.force;
|
2995 |
|
2996 | const replace = to.replace === true;
|
2997 | const shouldRedirect = handleRedirectRecord(targetLocation);
|
2998 | if (shouldRedirect)
|
2999 | return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
|
3000 | state: data,
|
3001 | force,
|
3002 | replace,
|
3003 | }),
|
3004 |
|
3005 | redirectedFrom || targetLocation);
|
3006 |
|
3007 | const toLocation = targetLocation;
|
3008 | toLocation.redirectedFrom = redirectedFrom;
|
3009 | let failure;
|
3010 | if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
|
3011 | failure = createRouterError(16 , { to: toLocation, from });
|
3012 |
|
3013 | handleScroll(from, from,
|
3014 |
|
3015 |
|
3016 | true,
|
3017 |
|
3018 |
|
3019 | false);
|
3020 | }
|
3021 | return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
|
3022 | .catch((error) => isNavigationFailure(error)
|
3023 | ?
|
3024 | isNavigationFailure(error, 2 )
|
3025 | ? error
|
3026 | : markAsReady(error)
|
3027 | :
|
3028 | triggerError(error, toLocation, from))
|
3029 | .then((failure) => {
|
3030 | if (failure) {
|
3031 | if (isNavigationFailure(failure, 2 )) {
|
3032 | if (
|
3033 | isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) &&
|
3034 |
|
3035 | redirectedFrom &&
|
3036 |
|
3037 | (redirectedFrom._count = redirectedFrom._count
|
3038 | ?
|
3039 | redirectedFrom._count + 1
|
3040 | : 1) > 10) {
|
3041 | warn(`Detected an infinite redirection in a navigation guard when going from "${from.fullPath}" to "${toLocation.fullPath}". Aborting to avoid a Stack Overflow. This will break in production if not fixed.`);
|
3042 | return Promise.reject(new Error('Infinite redirect in navigation guard'));
|
3043 | }
|
3044 | return pushWithRedirect(
|
3045 |
|
3046 | assign(locationAsObject(failure.to), {
|
3047 | state: data,
|
3048 | force,
|
3049 | replace,
|
3050 | }),
|
3051 |
|
3052 | redirectedFrom || toLocation);
|
3053 | }
|
3054 | }
|
3055 | else {
|
3056 |
|
3057 | failure = finalizeNavigation(toLocation, from, true, replace, data);
|
3058 | }
|
3059 | triggerAfterEach(toLocation, from, failure);
|
3060 | return failure;
|
3061 | });
|
3062 | }
|
3063 | |
3064 |
|
3065 |
|
3066 |
|
3067 |
|
3068 | function checkCanceledNavigationAndReject(to, from) {
|
3069 | const error = checkCanceledNavigation(to, from);
|
3070 | return error ? Promise.reject(error) : Promise.resolve();
|
3071 | }
|
3072 |
|
3073 | function navigate(to, from) {
|
3074 | let guards;
|
3075 | const [leavingRecords, updatingRecords, enteringRecords] = extractChangingRecords(to, from);
|
3076 |
|
3077 | guards = extractComponentsGuards(leavingRecords.reverse(), 'beforeRouteLeave', to, from);
|
3078 |
|
3079 | for (const record of leavingRecords) {
|
3080 | record.leaveGuards.forEach(guard => {
|
3081 | guards.push(guardToPromiseFn(guard, to, from));
|
3082 | });
|
3083 | }
|
3084 | const canceledNavigationCheck = checkCanceledNavigationAndReject.bind(null, to, from);
|
3085 | guards.push(canceledNavigationCheck);
|
3086 |
|
3087 | return (runGuardQueue(guards)
|
3088 | .then(() => {
|
3089 |
|
3090 | guards = [];
|
3091 | for (const guard of beforeGuards.list()) {
|
3092 | guards.push(guardToPromiseFn(guard, to, from));
|
3093 | }
|
3094 | guards.push(canceledNavigationCheck);
|
3095 | return runGuardQueue(guards);
|
3096 | })
|
3097 | .then(() => {
|
3098 |
|
3099 | guards = extractComponentsGuards(updatingRecords, 'beforeRouteUpdate', to, from);
|
3100 | for (const record of updatingRecords) {
|
3101 | record.updateGuards.forEach(guard => {
|
3102 | guards.push(guardToPromiseFn(guard, to, from));
|
3103 | });
|
3104 | }
|
3105 | guards.push(canceledNavigationCheck);
|
3106 |
|
3107 | return runGuardQueue(guards);
|
3108 | })
|
3109 | .then(() => {
|
3110 |
|
3111 | guards = [];
|
3112 | for (const record of to.matched) {
|
3113 |
|
3114 | if (record.beforeEnter && !from.matched.includes(record)) {
|
3115 | if (Array.isArray(record.beforeEnter)) {
|
3116 | for (const beforeEnter of record.beforeEnter)
|
3117 | guards.push(guardToPromiseFn(beforeEnter, to, from));
|
3118 | }
|
3119 | else {
|
3120 | guards.push(guardToPromiseFn(record.beforeEnter, to, from));
|
3121 | }
|
3122 | }
|
3123 | }
|
3124 | guards.push(canceledNavigationCheck);
|
3125 |
|
3126 | return runGuardQueue(guards);
|
3127 | })
|
3128 | .then(() => {
|
3129 |
|
3130 |
|
3131 | to.matched.forEach(record => (record.enterCallbacks = {}));
|
3132 |
|
3133 | guards = extractComponentsGuards(enteringRecords, 'beforeRouteEnter', to, from);
|
3134 | guards.push(canceledNavigationCheck);
|
3135 |
|
3136 | return runGuardQueue(guards);
|
3137 | })
|
3138 | .then(() => {
|
3139 |
|
3140 | guards = [];
|
3141 | for (const guard of beforeResolveGuards.list()) {
|
3142 | guards.push(guardToPromiseFn(guard, to, from));
|
3143 | }
|
3144 | guards.push(canceledNavigationCheck);
|
3145 | return runGuardQueue(guards);
|
3146 | })
|
3147 |
|
3148 | .catch(err => isNavigationFailure(err, 8 )
|
3149 | ? err
|
3150 | : Promise.reject(err)));
|
3151 | }
|
3152 | function triggerAfterEach(to, from, failure) {
|
3153 |
|
3154 |
|
3155 | for (const guard of afterGuards.list())
|
3156 | guard(to, from, failure);
|
3157 | }
|
3158 | |
3159 |
|
3160 |
|
3161 |
|
3162 |
|
3163 | function finalizeNavigation(toLocation, from, isPush, replace, data) {
|
3164 |
|
3165 | const error = checkCanceledNavigation(toLocation, from);
|
3166 | if (error)
|
3167 | return error;
|
3168 |
|
3169 | const isFirstNavigation = from === START_LOCATION_NORMALIZED;
|
3170 | const state = !isBrowser ? {} : history.state;
|
3171 |
|
3172 |
|
3173 | if (isPush) {
|
3174 |
|
3175 |
|
3176 | if (replace || isFirstNavigation)
|
3177 | routerHistory.replace(toLocation.fullPath, assign({
|
3178 | scroll: isFirstNavigation && state && state.scroll,
|
3179 | }, data));
|
3180 | else
|
3181 | routerHistory.push(toLocation.fullPath, data);
|
3182 | }
|
3183 |
|
3184 | currentRoute.value = toLocation;
|
3185 | handleScroll(toLocation, from, isPush, isFirstNavigation);
|
3186 | markAsReady();
|
3187 | }
|
3188 | let removeHistoryListener;
|
3189 |
|
3190 | function setupListeners() {
|
3191 |
|
3192 | if (removeHistoryListener)
|
3193 | return;
|
3194 | removeHistoryListener = routerHistory.listen((to, _from, info) => {
|
3195 |
|
3196 | const toLocation = resolve(to);
|
3197 |
|
3198 |
|
3199 |
|
3200 | const shouldRedirect = handleRedirectRecord(toLocation);
|
3201 | if (shouldRedirect) {
|
3202 | pushWithRedirect(assign(shouldRedirect, { replace: true }), toLocation).catch(noop);
|
3203 | return;
|
3204 | }
|
3205 | pendingLocation = toLocation;
|
3206 | const from = currentRoute.value;
|
3207 |
|
3208 | if (isBrowser) {
|
3209 | saveScrollPosition(getScrollKey(from.fullPath, info.delta), computeScrollPosition());
|
3210 | }
|
3211 | navigate(toLocation, from)
|
3212 | .catch((error) => {
|
3213 | if (isNavigationFailure(error, 4 | 8 )) {
|
3214 | return error;
|
3215 | }
|
3216 | if (isNavigationFailure(error, 2 )) {
|
3217 |
|
3218 |
|
3219 |
|
3220 |
|
3221 |
|
3222 |
|
3223 |
|
3224 |
|
3225 |
|
3226 | pushWithRedirect(error.to, toLocation
|
3227 |
|
3228 | )
|
3229 | .then(failure => {
|
3230 |
|
3231 |
|
3232 |
|
3233 | if (isNavigationFailure(failure, 4 |
|
3234 | 16 ) &&
|
3235 | !info.delta &&
|
3236 | info.type === NavigationType.pop) {
|
3237 | routerHistory.go(-1, false);
|
3238 | }
|
3239 | })
|
3240 | .catch(noop);
|
3241 |
|
3242 | return Promise.reject();
|
3243 | }
|
3244 |
|
3245 | if (info.delta)
|
3246 | routerHistory.go(-info.delta, false);
|
3247 |
|
3248 | return triggerError(error, toLocation, from);
|
3249 | })
|
3250 | .then((failure) => {
|
3251 | failure =
|
3252 | failure ||
|
3253 | finalizeNavigation(
|
3254 |
|
3255 | toLocation, from, false);
|
3256 |
|
3257 | if (failure) {
|
3258 | if (info.delta) {
|
3259 | routerHistory.go(-info.delta, false);
|
3260 | }
|
3261 | else if (info.type === NavigationType.pop &&
|
3262 | isNavigationFailure(failure, 4 | 16 )) {
|
3263 |
|
3264 |
|
3265 | routerHistory.go(-1, false);
|
3266 | }
|
3267 | }
|
3268 | triggerAfterEach(toLocation, from, failure);
|
3269 | })
|
3270 | .catch(noop);
|
3271 | });
|
3272 | }
|
3273 |
|
3274 | let readyHandlers = useCallbacks();
|
3275 | let errorHandlers = useCallbacks();
|
3276 | let ready;
|
3277 | |
3278 |
|
3279 |
|
3280 |
|
3281 |
|
3282 |
|
3283 |
|
3284 |
|
3285 | function triggerError(error, to, from) {
|
3286 | markAsReady(error);
|
3287 | const list = errorHandlers.list();
|
3288 | if (list.length) {
|
3289 | list.forEach(handler => handler(error, to, from));
|
3290 | }
|
3291 | else {
|
3292 | {
|
3293 | warn('uncaught error during route navigation:');
|
3294 | }
|
3295 | console.error(error);
|
3296 | }
|
3297 | return Promise.reject(error);
|
3298 | }
|
3299 | function isReady() {
|
3300 | if (ready && currentRoute.value !== START_LOCATION_NORMALIZED)
|
3301 | return Promise.resolve();
|
3302 | return new Promise((resolve, reject) => {
|
3303 | readyHandlers.add([resolve, reject]);
|
3304 | });
|
3305 | }
|
3306 | function markAsReady(err) {
|
3307 | if (!ready) {
|
3308 |
|
3309 | ready = !err;
|
3310 | setupListeners();
|
3311 | readyHandlers
|
3312 | .list()
|
3313 | .forEach(([resolve, reject]) => (err ? reject(err) : resolve()));
|
3314 | readyHandlers.reset();
|
3315 | }
|
3316 | return err;
|
3317 | }
|
3318 |
|
3319 | function handleScroll(to, from, isPush, isFirstNavigation) {
|
3320 | const { scrollBehavior } = options;
|
3321 | if (!isBrowser || !scrollBehavior)
|
3322 | return Promise.resolve();
|
3323 | const scrollPosition = (!isPush && getSavedScrollPosition(getScrollKey(to.fullPath, 0))) ||
|
3324 | ((isFirstNavigation || !isPush) &&
|
3325 | history.state &&
|
3326 | history.state.scroll) ||
|
3327 | null;
|
3328 | return vue.nextTick()
|
3329 | .then(() => scrollBehavior(to, from, scrollPosition))
|
3330 | .then(position => position && scrollToPosition(position))
|
3331 | .catch(err => triggerError(err, to, from));
|
3332 | }
|
3333 | const go = (delta) => routerHistory.go(delta);
|
3334 | let started;
|
3335 | const installedApps = new Set();
|
3336 | const router = {
|
3337 | currentRoute,
|
3338 | addRoute,
|
3339 | removeRoute,
|
3340 | hasRoute,
|
3341 | getRoutes,
|
3342 | resolve,
|
3343 | options,
|
3344 | push,
|
3345 | replace,
|
3346 | go,
|
3347 | back: () => go(-1),
|
3348 | forward: () => go(1),
|
3349 | beforeEach: beforeGuards.add,
|
3350 | beforeResolve: beforeResolveGuards.add,
|
3351 | afterEach: afterGuards.add,
|
3352 | onError: errorHandlers.add,
|
3353 | isReady,
|
3354 | install(app) {
|
3355 | const router = this;
|
3356 | app.component('RouterLink', RouterLink);
|
3357 | app.component('RouterView', RouterView);
|
3358 | app.config.globalProperties.$router = router;
|
3359 | Object.defineProperty(app.config.globalProperties, '$route', {
|
3360 | enumerable: true,
|
3361 | get: () => vue.unref(currentRoute),
|
3362 | });
|
3363 | // this initial navigation is only necessary on client, on server it doesn't
|
3364 | // make sense because it will create an extra unnecessary navigation and could
|
3365 | // lead to problems
|
3366 | if (isBrowser &&
|
3367 | // used for the initial navigation client side to avoid pushing
|
3368 | // multiple times when the router is used in multiple apps
|
3369 | !started &&
|
3370 | currentRoute.value === START_LOCATION_NORMALIZED) {
|
3371 |
|
3372 | started = true;
|
3373 | push(routerHistory.location).catch(err => {
|
3374 | warn('Unexpected error when starting the router:', err);
|
3375 | });
|
3376 | }
|
3377 | const reactiveRoute = {};
|
3378 | for (const key in START_LOCATION_NORMALIZED) {
|
3379 |
|
3380 | reactiveRoute[key] = vue.computed(() => currentRoute.value[key]);
|
3381 | }
|
3382 | app.provide(routerKey, router);
|
3383 | app.provide(routeLocationKey, vue.reactive(reactiveRoute));
|
3384 | app.provide(routerViewLocationKey, currentRoute);
|
3385 | const unmountApp = app.unmount;
|
3386 | installedApps.add(app);
|
3387 | app.unmount = function () {
|
3388 | installedApps.delete(app);
|
3389 |
|
3390 | if (installedApps.size < 1) {
|
3391 |
|
3392 | pendingLocation = START_LOCATION_NORMALIZED;
|
3393 | removeHistoryListener && removeHistoryListener();
|
3394 | removeHistoryListener = null;
|
3395 | currentRoute.value = START_LOCATION_NORMALIZED;
|
3396 | started = false;
|
3397 | ready = false;
|
3398 | }
|
3399 | unmountApp();
|
3400 | };
|
3401 | if (isBrowser) {
|
3402 | addDevtools(app, router, matcher);
|
3403 | }
|
3404 | },
|
3405 | };
|
3406 | return router;
|
3407 | }
|
3408 | function runGuardQueue(guards) {
|
3409 | return guards.reduce((promise, guard) => promise.then(() => guard()), Promise.resolve());
|
3410 | }
|
3411 | function extractChangingRecords(to, from) {
|
3412 | const leavingRecords = [];
|
3413 | const updatingRecords = [];
|
3414 | const enteringRecords = [];
|
3415 | const len = Math.max(from.matched.length, to.matched.length);
|
3416 | for (let i = 0; i < len; i++) {
|
3417 | const recordFrom = from.matched[i];
|
3418 | if (recordFrom) {
|
3419 | if (to.matched.find(record => isSameRouteRecord(record, recordFrom)))
|
3420 | updatingRecords.push(recordFrom);
|
3421 | else
|
3422 | leavingRecords.push(recordFrom);
|
3423 | }
|
3424 | const recordTo = to.matched[i];
|
3425 | if (recordTo) {
|
3426 |
|
3427 | if (!from.matched.find(record => isSameRouteRecord(record, recordTo))) {
|
3428 | enteringRecords.push(recordTo);
|
3429 | }
|
3430 | }
|
3431 | }
|
3432 | return [leavingRecords, updatingRecords, enteringRecords];
|
3433 | }
|
3434 |
|
3435 |
|
3436 |
|
3437 |
|
3438 |
|
3439 | function useRouter() {
|
3440 | return vue.inject(routerKey);
|
3441 | }
|
3442 |
|
3443 |
|
3444 |
|
3445 |
|
3446 | function useRoute() {
|
3447 | return vue.inject(routeLocationKey);
|
3448 | }
|
3449 |
|
3450 | exports.RouterLink = RouterLink;
|
3451 | exports.RouterView = RouterView;
|
3452 | exports.START_LOCATION = START_LOCATION_NORMALIZED;
|
3453 | exports.createMemoryHistory = createMemoryHistory;
|
3454 | exports.createRouter = createRouter;
|
3455 | exports.createRouterMatcher = createRouterMatcher;
|
3456 | exports.createWebHashHistory = createWebHashHistory;
|
3457 | exports.createWebHistory = createWebHistory;
|
3458 | exports.isNavigationFailure = isNavigationFailure;
|
3459 | exports.matchedRouteKey = matchedRouteKey;
|
3460 | exports.onBeforeRouteLeave = onBeforeRouteLeave;
|
3461 | exports.onBeforeRouteUpdate = onBeforeRouteUpdate;
|
3462 | exports.parseQuery = parseQuery;
|
3463 | exports.routeLocationKey = routeLocationKey;
|
3464 | exports.routerKey = routerKey;
|
3465 | exports.routerViewLocationKey = routerViewLocationKey;
|
3466 | exports.stringifyQuery = stringifyQuery;
|
3467 | exports.useLink = useLink;
|
3468 | exports.useRoute = useRoute;
|
3469 | exports.useRouter = useRouter;
|
3470 | exports.viewDepthKey = viewDepthKey;
|