kdu-router 4.0.16 → 4.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/kdu-router.cjs +3610 -0
- package/dist/kdu-router.cjs.js +1 -3484
- package/dist/kdu-router.cjs.prod.js +1 -2759
- package/dist/kdu-router.esm-browser.js +330 -205
- package/dist/kdu-router.esm-bundler.js +1 -3478
- package/dist/kdu-router.global.js +361 -207
- package/dist/kdu-router.global.prod.js +3 -3
- package/dist/kdu-router.mjs +3603 -0
- package/dist/kdu-router.node.mjs +2 -0
- package/dist/kdu-router.prod.cjs +2841 -0
- package/index.js +7 -0
- package/package.json +45 -52
- package/LICENSE +0 -21
- package/README.md +0 -17
- package/dist/kdu-router.d.ts +0 -1349
|
@@ -1,70 +1,33 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* kdu-router v4.
|
|
3
|
-
* (c)
|
|
2
|
+
* kdu-router v4.1.6
|
|
3
|
+
* (c) 2023 NKDuy
|
|
4
4
|
* @license MIT
|
|
5
5
|
*/
|
|
6
6
|
import { getCurrentInstance, inject, onUnmounted, onDeactivated, onActivated, computed, unref, watchEffect, defineComponent, reactive, h, provide, ref, watch, shallowRef, nextTick } from 'kdu';
|
|
7
7
|
import { setupDevtoolsPlugin } from '@kdujs/devtools-api';
|
|
8
8
|
|
|
9
|
-
const hasSymbol = typeof Symbol === 'function' && typeof Symbol.toStringTag === 'symbol';
|
|
10
|
-
const PolySymbol = (name) =>
|
|
11
|
-
// kr = kdu router
|
|
12
|
-
hasSymbol
|
|
13
|
-
? Symbol('[kdu-router]: ' + name )
|
|
14
|
-
: ('[kdu-router]: ' ) + name;
|
|
15
|
-
// rvlm = Router View Location Matched
|
|
16
|
-
/**
|
|
17
|
-
* RouteRecord being rendered by the closest ancestor Router View. Used for
|
|
18
|
-
* `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
|
|
19
|
-
* Location Matched
|
|
20
|
-
*
|
|
21
|
-
* @internal
|
|
22
|
-
*/
|
|
23
|
-
const matchedRouteKey = /*#__PURE__*/ PolySymbol('router view location matched' );
|
|
24
|
-
/**
|
|
25
|
-
* Allows overriding the router view depth to control which component in
|
|
26
|
-
* `matched` is rendered. rvd stands for Router View Depth
|
|
27
|
-
*
|
|
28
|
-
* @internal
|
|
29
|
-
*/
|
|
30
|
-
const viewDepthKey = /*#__PURE__*/ PolySymbol('router view depth' );
|
|
31
|
-
/**
|
|
32
|
-
* Allows overriding the router instance returned by `useRouter` in tests. r
|
|
33
|
-
* stands for router
|
|
34
|
-
*
|
|
35
|
-
* @internal
|
|
36
|
-
*/
|
|
37
|
-
const routerKey = /*#__PURE__*/ PolySymbol('router' );
|
|
38
|
-
/**
|
|
39
|
-
* Allows overriding the current route returned by `useRoute` in tests. rl
|
|
40
|
-
* stands for route location
|
|
41
|
-
*
|
|
42
|
-
* @internal
|
|
43
|
-
*/
|
|
44
|
-
const routeLocationKey = /*#__PURE__*/ PolySymbol('route location' );
|
|
45
|
-
/**
|
|
46
|
-
* Allows overriding the current route used by router-view. Internally this is
|
|
47
|
-
* used when the `route` prop is passed.
|
|
48
|
-
*
|
|
49
|
-
* @internal
|
|
50
|
-
*/
|
|
51
|
-
const routerViewLocationKey = /*#__PURE__*/ PolySymbol('router view location' );
|
|
52
|
-
|
|
53
9
|
const isBrowser = typeof window !== 'undefined';
|
|
54
10
|
|
|
55
11
|
function isESModule(obj) {
|
|
56
|
-
return obj.__esModule ||
|
|
12
|
+
return obj.__esModule || obj[Symbol.toStringTag] === 'Module';
|
|
57
13
|
}
|
|
58
14
|
const assign = Object.assign;
|
|
59
15
|
function applyToParams(fn, params) {
|
|
60
16
|
const newParams = {};
|
|
61
17
|
for (const key in params) {
|
|
62
18
|
const value = params[key];
|
|
63
|
-
newParams[key] =
|
|
19
|
+
newParams[key] = isArray(value)
|
|
20
|
+
? value.map(fn)
|
|
21
|
+
: fn(value);
|
|
64
22
|
}
|
|
65
23
|
return newParams;
|
|
66
24
|
}
|
|
67
|
-
const noop = () => { };
|
|
25
|
+
const noop = () => { };
|
|
26
|
+
/**
|
|
27
|
+
* Typesafe alternative to Array.isArray
|
|
28
|
+
* https://github.com/microsoft/TypeScript/pull/48228
|
|
29
|
+
*/
|
|
30
|
+
const isArray = Array.isArray;
|
|
68
31
|
|
|
69
32
|
function warn(msg) {
|
|
70
33
|
// avoid using ...args as it breaks in older Edge builds
|
|
@@ -75,7 +38,7 @@ function warn(msg) {
|
|
|
75
38
|
const TRAILING_SLASH_RE = /\/$/;
|
|
76
39
|
const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
|
|
77
40
|
/**
|
|
78
|
-
* Transforms
|
|
41
|
+
* Transforms a URI into a normalized history location
|
|
79
42
|
*
|
|
80
43
|
* @param parseQuery
|
|
81
44
|
* @param location - URI to normalize
|
|
@@ -86,8 +49,13 @@ const removeTrailingSlash = (path) => path.replace(TRAILING_SLASH_RE, '');
|
|
|
86
49
|
function parseURL(parseQuery, location, currentLocation = '/') {
|
|
87
50
|
let path, query = {}, searchString = '', hash = '';
|
|
88
51
|
// Could use URL and URLSearchParams but IE 11 doesn't support it
|
|
89
|
-
|
|
90
|
-
const hashPos = location.indexOf('#'
|
|
52
|
+
// TODO: move to new URL()
|
|
53
|
+
const hashPos = location.indexOf('#');
|
|
54
|
+
let searchPos = location.indexOf('?');
|
|
55
|
+
// the hash appears before the search, so it's not part of the search string
|
|
56
|
+
if (hashPos < searchPos && hashPos >= 0) {
|
|
57
|
+
searchPos = -1;
|
|
58
|
+
}
|
|
91
59
|
if (searchPos > -1) {
|
|
92
60
|
path = location.slice(0, searchPos);
|
|
93
61
|
searchString = location.slice(searchPos + 1, hashPos > -1 ? hashPos : location.length);
|
|
@@ -119,8 +87,7 @@ function stringifyURL(stringifyQuery, location) {
|
|
|
119
87
|
return location.path + (query && '?') + query + (location.hash || '');
|
|
120
88
|
}
|
|
121
89
|
/**
|
|
122
|
-
* Strips off the base from the beginning of a location.pathname in a non
|
|
123
|
-
* case-sensitive way.
|
|
90
|
+
* Strips off the base from the beginning of a location.pathname in a non-case-sensitive way.
|
|
124
91
|
*
|
|
125
92
|
* @param pathname - location.pathname
|
|
126
93
|
* @param base - base to strip off
|
|
@@ -172,9 +139,9 @@ function isSameRouteLocationParams(a, b) {
|
|
|
172
139
|
return true;
|
|
173
140
|
}
|
|
174
141
|
function isSameRouteLocationParamsValue(a, b) {
|
|
175
|
-
return
|
|
142
|
+
return isArray(a)
|
|
176
143
|
? isEquivalentArray(a, b)
|
|
177
|
-
:
|
|
144
|
+
: isArray(b)
|
|
178
145
|
? isEquivalentArray(b, a)
|
|
179
146
|
: a === b;
|
|
180
147
|
}
|
|
@@ -186,7 +153,7 @@ function isSameRouteLocationParamsValue(a, b) {
|
|
|
186
153
|
* @param b - array of values or a single value
|
|
187
154
|
*/
|
|
188
155
|
function isEquivalentArray(a, b) {
|
|
189
|
-
return
|
|
156
|
+
return isArray(b)
|
|
190
157
|
? a.length === b.length && a.every((value, i) => value === b[i])
|
|
191
158
|
: a.length === 1 && a[0] === b;
|
|
192
159
|
}
|
|
@@ -212,18 +179,24 @@ function resolveRelativePath(to, from) {
|
|
|
212
179
|
let segment;
|
|
213
180
|
for (toPosition = 0; toPosition < toSegments.length; toPosition++) {
|
|
214
181
|
segment = toSegments[toPosition];
|
|
215
|
-
//
|
|
216
|
-
if (
|
|
182
|
+
// we stay on the same position
|
|
183
|
+
if (segment === '.')
|
|
217
184
|
continue;
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
185
|
+
// go up in the from array
|
|
186
|
+
if (segment === '..') {
|
|
187
|
+
// we can't go below zero, but we still need to increment toPosition
|
|
188
|
+
if (position > 1)
|
|
189
|
+
position--;
|
|
190
|
+
// continue
|
|
191
|
+
}
|
|
192
|
+
// we reached a non-relative path, we stop here
|
|
221
193
|
else
|
|
222
194
|
break;
|
|
223
195
|
}
|
|
224
196
|
return (fromSegments.slice(0, position).join('/') +
|
|
225
197
|
'/' +
|
|
226
198
|
toSegments
|
|
199
|
+
// ensure we use at least the last element in the toSegments
|
|
227
200
|
.slice(toPosition - (toPosition === toSegments.length ? 1 : 0))
|
|
228
201
|
.join('/'));
|
|
229
202
|
}
|
|
@@ -452,7 +425,7 @@ function useHistoryListeners(base, historyState, currentLocation, replace) {
|
|
|
452
425
|
pauseState = currentLocation.value;
|
|
453
426
|
}
|
|
454
427
|
function listen(callback) {
|
|
455
|
-
//
|
|
428
|
+
// set up the listener and prepare teardown callbacks
|
|
456
429
|
listeners.push(callback);
|
|
457
430
|
const teardown = () => {
|
|
458
431
|
const index = listeners.indexOf(callback);
|
|
@@ -475,7 +448,7 @@ function useHistoryListeners(base, historyState, currentLocation, replace) {
|
|
|
475
448
|
window.removeEventListener('popstate', popStateHandler);
|
|
476
449
|
window.removeEventListener('beforeunload', beforeUnloadListener);
|
|
477
450
|
}
|
|
478
|
-
//
|
|
451
|
+
// set up the listeners and prepare teardown callbacks
|
|
479
452
|
window.addEventListener('popstate', popStateHandler);
|
|
480
453
|
window.addEventListener('beforeunload', beforeUnloadListener);
|
|
481
454
|
return {
|
|
@@ -513,14 +486,14 @@ function useHistoryStateNavigation(base) {
|
|
|
513
486
|
// the length is off by one, we need to decrease it
|
|
514
487
|
position: history.length - 1,
|
|
515
488
|
replaced: true,
|
|
516
|
-
// don't add a scroll as the user may have an anchor and we want
|
|
489
|
+
// don't add a scroll as the user may have an anchor, and we want
|
|
517
490
|
// scrollBehavior to be triggered without a saved position
|
|
518
491
|
scroll: null,
|
|
519
492
|
}, true);
|
|
520
493
|
}
|
|
521
494
|
function changeLocation(to, state, replace) {
|
|
522
495
|
/**
|
|
523
|
-
* if a base tag is provided and we are on a normal domain, we have to
|
|
496
|
+
* if a base tag is provided, and we are on a normal domain, we have to
|
|
524
497
|
* respect the provided `base` attribute because pushState() will use it and
|
|
525
498
|
* potentially erase anything before the `#` where a base of
|
|
526
499
|
* `/folder/#` but a base of `/` would erase the `/folder/` section. If
|
|
@@ -614,7 +587,7 @@ function createWebHistory(base) {
|
|
|
614
587
|
}
|
|
615
588
|
|
|
616
589
|
/**
|
|
617
|
-
* Creates
|
|
590
|
+
* Creates an in-memory based history. The main purpose of this history is to handle SSR. It starts in a special location that is nowhere.
|
|
618
591
|
* It's up to the user to replace that location with the starter location by either calling `router.push` or `router.replace`.
|
|
619
592
|
*
|
|
620
593
|
* @param base - Base applied to all urls, defaults to '/'
|
|
@@ -699,15 +672,13 @@ function createMemoryHistory(base = '') {
|
|
|
699
672
|
}
|
|
700
673
|
|
|
701
674
|
/**
|
|
702
|
-
* Creates a hash history. Useful for web applications with no host (e.g.
|
|
703
|
-
*
|
|
675
|
+
* Creates a hash history. Useful for web applications with no host (e.g. `file://`) or when configuring a server to
|
|
676
|
+
* handle any URL is not possible.
|
|
704
677
|
*
|
|
705
|
-
* @param base - optional base to provide. Defaults to `location.pathname +
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
*
|
|
709
|
-
* `href` value **has to match this parameter** (ignoring anything after the
|
|
710
|
-
* `#`).
|
|
678
|
+
* @param base - optional base to provide. Defaults to `location.pathname + location.search` If there is a `<base>` tag
|
|
679
|
+
* in the `head`, its value will be ignored in favor of this parameter **but note it affects all the history.pushState()
|
|
680
|
+
* calls**, meaning that if you use a `<base>` tag, it's `href` value **has to match this parameter** (ignoring anything
|
|
681
|
+
* after the `#`).
|
|
711
682
|
*
|
|
712
683
|
* @example
|
|
713
684
|
* ```js
|
|
@@ -772,7 +743,7 @@ const START_LOCATION_NORMALIZED = {
|
|
|
772
743
|
redirectedFrom: undefined,
|
|
773
744
|
};
|
|
774
745
|
|
|
775
|
-
const NavigationFailureSymbol =
|
|
746
|
+
const NavigationFailureSymbol = Symbol('navigation failure' );
|
|
776
747
|
/**
|
|
777
748
|
* Enumeration with all possible types for navigation failures. Can be passed to
|
|
778
749
|
* {@link isNavigationFailure} to check for specific failures.
|
|
@@ -797,21 +768,21 @@ var NavigationFailureType;
|
|
|
797
768
|
})(NavigationFailureType || (NavigationFailureType = {}));
|
|
798
769
|
// DEV only debug messages
|
|
799
770
|
const ErrorTypeMessages = {
|
|
800
|
-
[1 /* MATCHER_NOT_FOUND */]({ location, currentLocation }) {
|
|
771
|
+
[1 /* ErrorTypes.MATCHER_NOT_FOUND */]({ location, currentLocation }) {
|
|
801
772
|
return `No match for\n ${JSON.stringify(location)}${currentLocation
|
|
802
773
|
? '\nwhile being at\n' + JSON.stringify(currentLocation)
|
|
803
774
|
: ''}`;
|
|
804
775
|
},
|
|
805
|
-
[2 /* NAVIGATION_GUARD_REDIRECT */]({ from, to, }) {
|
|
776
|
+
[2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */]({ from, to, }) {
|
|
806
777
|
return `Redirected from "${from.fullPath}" to "${stringifyRoute(to)}" via a navigation guard.`;
|
|
807
778
|
},
|
|
808
|
-
[4 /* NAVIGATION_ABORTED */]({ from, to }) {
|
|
779
|
+
[4 /* ErrorTypes.NAVIGATION_ABORTED */]({ from, to }) {
|
|
809
780
|
return `Navigation aborted from "${from.fullPath}" to "${to.fullPath}" via a navigation guard.`;
|
|
810
781
|
},
|
|
811
|
-
[8 /* NAVIGATION_CANCELLED */]({ from, to }) {
|
|
782
|
+
[8 /* ErrorTypes.NAVIGATION_CANCELLED */]({ from, to }) {
|
|
812
783
|
return `Navigation cancelled from "${from.fullPath}" to "${to.fullPath}" with a new navigation.`;
|
|
813
784
|
},
|
|
814
|
-
[16 /* NAVIGATION_DUPLICATED */]({ from, to }) {
|
|
785
|
+
[16 /* ErrorTypes.NAVIGATION_DUPLICATED */]({ from, to }) {
|
|
815
786
|
return `Avoided redundant navigation to current location: "${from.fullPath}".`;
|
|
816
787
|
},
|
|
817
788
|
};
|
|
@@ -843,7 +814,7 @@ function stringifyRoute(to) {
|
|
|
843
814
|
return JSON.stringify(location, null, 2);
|
|
844
815
|
}
|
|
845
816
|
|
|
846
|
-
// default pattern for a param: non
|
|
817
|
+
// default pattern for a param: non-greedy everything but /
|
|
847
818
|
const BASE_PARAM_PATTERN = '[^/]+?';
|
|
848
819
|
const BASE_PATH_PARSER_OPTIONS = {
|
|
849
820
|
sensitive: false,
|
|
@@ -870,23 +841,23 @@ function tokensToParser(segments, extraOptions) {
|
|
|
870
841
|
const keys = [];
|
|
871
842
|
for (const segment of segments) {
|
|
872
843
|
// the root segment needs special treatment
|
|
873
|
-
const segmentScores = segment.length ? [] : [90 /* Root */];
|
|
844
|
+
const segmentScores = segment.length ? [] : [90 /* PathScore.Root */];
|
|
874
845
|
// allow trailing slash
|
|
875
846
|
if (options.strict && !segment.length)
|
|
876
847
|
pattern += '/';
|
|
877
848
|
for (let tokenIndex = 0; tokenIndex < segment.length; tokenIndex++) {
|
|
878
849
|
const token = segment[tokenIndex];
|
|
879
|
-
// resets the score if we are inside a sub
|
|
880
|
-
let subSegmentScore = 40 /* Segment */ +
|
|
881
|
-
(options.sensitive ? 0.25 /* BonusCaseSensitive */ : 0);
|
|
882
|
-
if (token.type === 0 /* Static */) {
|
|
850
|
+
// resets the score if we are inside a sub-segment /:a-other-:b
|
|
851
|
+
let subSegmentScore = 40 /* PathScore.Segment */ +
|
|
852
|
+
(options.sensitive ? 0.25 /* PathScore.BonusCaseSensitive */ : 0);
|
|
853
|
+
if (token.type === 0 /* TokenType.Static */) {
|
|
883
854
|
// prepend the slash if we are starting a new segment
|
|
884
855
|
if (!tokenIndex)
|
|
885
856
|
pattern += '/';
|
|
886
857
|
pattern += token.value.replace(REGEX_CHARS_RE, '\\$&');
|
|
887
|
-
subSegmentScore += 40 /* Static */;
|
|
858
|
+
subSegmentScore += 40 /* PathScore.Static */;
|
|
888
859
|
}
|
|
889
|
-
else if (token.type === 1 /* Param */) {
|
|
860
|
+
else if (token.type === 1 /* TokenType.Param */) {
|
|
890
861
|
const { value, repeatable, optional, regexp } = token;
|
|
891
862
|
keys.push({
|
|
892
863
|
name: value,
|
|
@@ -896,7 +867,7 @@ function tokensToParser(segments, extraOptions) {
|
|
|
896
867
|
const re = regexp ? regexp : BASE_PARAM_PATTERN;
|
|
897
868
|
// the user provided a custom regexp /:id(\\d+)
|
|
898
869
|
if (re !== BASE_PARAM_PATTERN) {
|
|
899
|
-
subSegmentScore += 10 /* BonusCustomRegExp */;
|
|
870
|
+
subSegmentScore += 10 /* PathScore.BonusCustomRegExp */;
|
|
900
871
|
// make sure the regexp is valid before using it
|
|
901
872
|
try {
|
|
902
873
|
new RegExp(`(${re})`);
|
|
@@ -919,13 +890,13 @@ function tokensToParser(segments, extraOptions) {
|
|
|
919
890
|
if (optional)
|
|
920
891
|
subPattern += '?';
|
|
921
892
|
pattern += subPattern;
|
|
922
|
-
subSegmentScore += 20 /* Dynamic */;
|
|
893
|
+
subSegmentScore += 20 /* PathScore.Dynamic */;
|
|
923
894
|
if (optional)
|
|
924
|
-
subSegmentScore += -8 /* BonusOptional */;
|
|
895
|
+
subSegmentScore += -8 /* PathScore.BonusOptional */;
|
|
925
896
|
if (repeatable)
|
|
926
|
-
subSegmentScore += -20 /* BonusRepeatable */;
|
|
897
|
+
subSegmentScore += -20 /* PathScore.BonusRepeatable */;
|
|
927
898
|
if (re === '.*')
|
|
928
|
-
subSegmentScore += -50 /* BonusWildcard */;
|
|
899
|
+
subSegmentScore += -50 /* PathScore.BonusWildcard */;
|
|
929
900
|
}
|
|
930
901
|
segmentScores.push(subSegmentScore);
|
|
931
902
|
}
|
|
@@ -936,7 +907,7 @@ function tokensToParser(segments, extraOptions) {
|
|
|
936
907
|
// only apply the strict bonus to the last score
|
|
937
908
|
if (options.strict && options.end) {
|
|
938
909
|
const i = score.length - 1;
|
|
939
|
-
score[i][score[i].length - 1] += 0.7000000000000001 /* BonusStrict */;
|
|
910
|
+
score[i][score[i].length - 1] += 0.7000000000000001 /* PathScore.BonusStrict */;
|
|
940
911
|
}
|
|
941
912
|
// TODO: dev only warn double trailing slash
|
|
942
913
|
if (!options.strict)
|
|
@@ -968,20 +939,22 @@ function tokensToParser(segments, extraOptions) {
|
|
|
968
939
|
path += '/';
|
|
969
940
|
avoidDuplicatedSlash = false;
|
|
970
941
|
for (const token of segment) {
|
|
971
|
-
if (token.type === 0 /* Static */) {
|
|
942
|
+
if (token.type === 0 /* TokenType.Static */) {
|
|
972
943
|
path += token.value;
|
|
973
944
|
}
|
|
974
|
-
else if (token.type === 1 /* Param */) {
|
|
945
|
+
else if (token.type === 1 /* TokenType.Param */) {
|
|
975
946
|
const { value, repeatable, optional } = token;
|
|
976
947
|
const param = value in params ? params[value] : '';
|
|
977
|
-
if (
|
|
948
|
+
if (isArray(param) && !repeatable) {
|
|
978
949
|
throw new Error(`Provided param "${value}" is an array but it is not repeatable (* or + modifiers)`);
|
|
979
|
-
|
|
950
|
+
}
|
|
951
|
+
const text = isArray(param)
|
|
952
|
+
? param.join('/')
|
|
953
|
+
: param;
|
|
980
954
|
if (!text) {
|
|
981
955
|
if (optional) {
|
|
982
|
-
// if we have more than one optional param like /:a?-static
|
|
983
|
-
|
|
984
|
-
if (segment.length < 2 && segments.length > 1) {
|
|
956
|
+
// if we have more than one optional param like /:a?-static we don't need to care about the optional param
|
|
957
|
+
if (segment.length < 2) {
|
|
985
958
|
// remove the last slash as we could be at the end
|
|
986
959
|
if (path.endsWith('/'))
|
|
987
960
|
path = path.slice(0, -1);
|
|
@@ -997,7 +970,8 @@ function tokensToParser(segments, extraOptions) {
|
|
|
997
970
|
}
|
|
998
971
|
}
|
|
999
972
|
}
|
|
1000
|
-
|
|
973
|
+
// avoid empty path when we have multiple optional params
|
|
974
|
+
return path || '/';
|
|
1001
975
|
}
|
|
1002
976
|
return {
|
|
1003
977
|
re,
|
|
@@ -1028,12 +1002,12 @@ function compareScoreArray(a, b) {
|
|
|
1028
1002
|
// if the last subsegment was Static, the shorter segments should be sorted first
|
|
1029
1003
|
// otherwise sort the longest segment first
|
|
1030
1004
|
if (a.length < b.length) {
|
|
1031
|
-
return a.length === 1 && a[0] === 40 /* Static */ + 40 /* Segment */
|
|
1005
|
+
return a.length === 1 && a[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */
|
|
1032
1006
|
? -1
|
|
1033
1007
|
: 1;
|
|
1034
1008
|
}
|
|
1035
1009
|
else if (a.length > b.length) {
|
|
1036
|
-
return b.length === 1 && b[0] === 40 /* Static */ + 40 /* Segment */
|
|
1010
|
+
return b.length === 1 && b[0] === 40 /* PathScore.Static */ + 40 /* PathScore.Segment */
|
|
1037
1011
|
? 1
|
|
1038
1012
|
: -1;
|
|
1039
1013
|
}
|
|
@@ -1084,7 +1058,7 @@ function isLastScoreNegative(score) {
|
|
|
1084
1058
|
}
|
|
1085
1059
|
|
|
1086
1060
|
const ROOT_TOKEN = {
|
|
1087
|
-
type: 0 /* Static */,
|
|
1061
|
+
type: 0 /* TokenType.Static */,
|
|
1088
1062
|
value: '',
|
|
1089
1063
|
};
|
|
1090
1064
|
const VALID_PARAM_RE = /[a-zA-Z0-9_]/;
|
|
@@ -1104,7 +1078,7 @@ function tokenizePath(path) {
|
|
|
1104
1078
|
function crash(message) {
|
|
1105
1079
|
throw new Error(`ERR (${state})/"${buffer}": ${message}`);
|
|
1106
1080
|
}
|
|
1107
|
-
let state = 0 /* Static */;
|
|
1081
|
+
let state = 0 /* TokenizerState.Static */;
|
|
1108
1082
|
let previousState = state;
|
|
1109
1083
|
const tokens = [];
|
|
1110
1084
|
// the segment will always be valid because we get into the initial state
|
|
@@ -1126,19 +1100,19 @@ function tokenizePath(path) {
|
|
|
1126
1100
|
function consumeBuffer() {
|
|
1127
1101
|
if (!buffer)
|
|
1128
1102
|
return;
|
|
1129
|
-
if (state === 0 /* Static */) {
|
|
1103
|
+
if (state === 0 /* TokenizerState.Static */) {
|
|
1130
1104
|
segment.push({
|
|
1131
|
-
type: 0 /* Static */,
|
|
1105
|
+
type: 0 /* TokenType.Static */,
|
|
1132
1106
|
value: buffer,
|
|
1133
1107
|
});
|
|
1134
1108
|
}
|
|
1135
|
-
else if (state === 1 /* Param */ ||
|
|
1136
|
-
state === 2 /* ParamRegExp */ ||
|
|
1137
|
-
state === 3 /* ParamRegExpEnd */) {
|
|
1109
|
+
else if (state === 1 /* TokenizerState.Param */ ||
|
|
1110
|
+
state === 2 /* TokenizerState.ParamRegExp */ ||
|
|
1111
|
+
state === 3 /* TokenizerState.ParamRegExpEnd */) {
|
|
1138
1112
|
if (segment.length > 1 && (char === '*' || char === '+'))
|
|
1139
1113
|
crash(`A repeatable param (${buffer}) must be alone in its segment. eg: '/:ids+.`);
|
|
1140
1114
|
segment.push({
|
|
1141
|
-
type: 1 /* Param */,
|
|
1115
|
+
type: 1 /* TokenType.Param */,
|
|
1142
1116
|
value: buffer,
|
|
1143
1117
|
regexp: customRe,
|
|
1144
1118
|
repeatable: char === '*' || char === '+',
|
|
@@ -1155,13 +1129,13 @@ function tokenizePath(path) {
|
|
|
1155
1129
|
}
|
|
1156
1130
|
while (i < path.length) {
|
|
1157
1131
|
char = path[i++];
|
|
1158
|
-
if (char === '\\' && state !== 2 /* ParamRegExp */) {
|
|
1132
|
+
if (char === '\\' && state !== 2 /* TokenizerState.ParamRegExp */) {
|
|
1159
1133
|
previousState = state;
|
|
1160
|
-
state = 4 /* EscapeNext */;
|
|
1134
|
+
state = 4 /* TokenizerState.EscapeNext */;
|
|
1161
1135
|
continue;
|
|
1162
1136
|
}
|
|
1163
1137
|
switch (state) {
|
|
1164
|
-
case 0 /* Static */:
|
|
1138
|
+
case 0 /* TokenizerState.Static */:
|
|
1165
1139
|
if (char === '/') {
|
|
1166
1140
|
if (buffer) {
|
|
1167
1141
|
consumeBuffer();
|
|
@@ -1170,32 +1144,32 @@ function tokenizePath(path) {
|
|
|
1170
1144
|
}
|
|
1171
1145
|
else if (char === ':') {
|
|
1172
1146
|
consumeBuffer();
|
|
1173
|
-
state = 1 /* Param */;
|
|
1147
|
+
state = 1 /* TokenizerState.Param */;
|
|
1174
1148
|
}
|
|
1175
1149
|
else {
|
|
1176
1150
|
addCharToBuffer();
|
|
1177
1151
|
}
|
|
1178
1152
|
break;
|
|
1179
|
-
case 4 /* EscapeNext */:
|
|
1153
|
+
case 4 /* TokenizerState.EscapeNext */:
|
|
1180
1154
|
addCharToBuffer();
|
|
1181
1155
|
state = previousState;
|
|
1182
1156
|
break;
|
|
1183
|
-
case 1 /* Param */:
|
|
1157
|
+
case 1 /* TokenizerState.Param */:
|
|
1184
1158
|
if (char === '(') {
|
|
1185
|
-
state = 2 /* ParamRegExp */;
|
|
1159
|
+
state = 2 /* TokenizerState.ParamRegExp */;
|
|
1186
1160
|
}
|
|
1187
1161
|
else if (VALID_PARAM_RE.test(char)) {
|
|
1188
1162
|
addCharToBuffer();
|
|
1189
1163
|
}
|
|
1190
1164
|
else {
|
|
1191
1165
|
consumeBuffer();
|
|
1192
|
-
state = 0 /* Static */;
|
|
1166
|
+
state = 0 /* TokenizerState.Static */;
|
|
1193
1167
|
// go back one character if we were not modifying
|
|
1194
1168
|
if (char !== '*' && char !== '?' && char !== '+')
|
|
1195
1169
|
i--;
|
|
1196
1170
|
}
|
|
1197
1171
|
break;
|
|
1198
|
-
case 2 /* ParamRegExp */:
|
|
1172
|
+
case 2 /* TokenizerState.ParamRegExp */:
|
|
1199
1173
|
// TODO: is it worth handling nested regexp? like :p(?:prefix_([^/]+)_suffix)
|
|
1200
1174
|
// it already works by escaping the closing )
|
|
1201
1175
|
// is this really something people need since you can also write
|
|
@@ -1205,16 +1179,16 @@ function tokenizePath(path) {
|
|
|
1205
1179
|
if (customRe[customRe.length - 1] == '\\')
|
|
1206
1180
|
customRe = customRe.slice(0, -1) + char;
|
|
1207
1181
|
else
|
|
1208
|
-
state = 3 /* ParamRegExpEnd */;
|
|
1182
|
+
state = 3 /* TokenizerState.ParamRegExpEnd */;
|
|
1209
1183
|
}
|
|
1210
1184
|
else {
|
|
1211
1185
|
customRe += char;
|
|
1212
1186
|
}
|
|
1213
1187
|
break;
|
|
1214
|
-
case 3 /* ParamRegExpEnd */:
|
|
1188
|
+
case 3 /* TokenizerState.ParamRegExpEnd */:
|
|
1215
1189
|
// same as finalizing a param
|
|
1216
1190
|
consumeBuffer();
|
|
1217
|
-
state = 0 /* Static */;
|
|
1191
|
+
state = 0 /* TokenizerState.Static */;
|
|
1218
1192
|
// go back one character if we were not modifying
|
|
1219
1193
|
if (char !== '*' && char !== '?' && char !== '+')
|
|
1220
1194
|
i--;
|
|
@@ -1225,7 +1199,7 @@ function tokenizePath(path) {
|
|
|
1225
1199
|
break;
|
|
1226
1200
|
}
|
|
1227
1201
|
}
|
|
1228
|
-
if (state === 2 /* ParamRegExp */)
|
|
1202
|
+
if (state === 2 /* TokenizerState.ParamRegExp */)
|
|
1229
1203
|
crash(`Unfinished custom RegExp for param "${buffer}"`);
|
|
1230
1204
|
consumeBuffer();
|
|
1231
1205
|
finalizeSegment();
|
|
@@ -1280,6 +1254,9 @@ function createRouterMatcher(routes, globalOptions) {
|
|
|
1280
1254
|
// used later on to remove by name
|
|
1281
1255
|
const isRootAdd = !originalRecord;
|
|
1282
1256
|
const mainNormalizedRecord = normalizeRouteRecord(record);
|
|
1257
|
+
{
|
|
1258
|
+
checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent);
|
|
1259
|
+
}
|
|
1283
1260
|
// we might be the child of an alias
|
|
1284
1261
|
mainNormalizedRecord.aliasOf = originalRecord && originalRecord.record;
|
|
1285
1262
|
const options = mergeOptions(globalOptions, record);
|
|
@@ -1323,11 +1300,11 @@ function createRouterMatcher(routes, globalOptions) {
|
|
|
1323
1300
|
throw new Error('Catch all routes ("*") must now be defined using a param with a custom regexp.\n' +
|
|
1324
1301
|
'See more at https://kdujs-router.web.app/guide/migration/#removed-star-or-catch-all-routes.');
|
|
1325
1302
|
}
|
|
1326
|
-
// create the object
|
|
1303
|
+
// create the object beforehand, so it can be passed to children
|
|
1327
1304
|
matcher = createRouteRecordMatcher(normalizedRecord, parent, options);
|
|
1328
1305
|
if (parent && path[0] === '/')
|
|
1329
1306
|
checkMissingParamsInAbsolutePath(matcher, parent);
|
|
1330
|
-
// if we are an alias we must tell the original record that we exist
|
|
1307
|
+
// if we are an alias we must tell the original record that we exist,
|
|
1331
1308
|
// so we can be removed
|
|
1332
1309
|
if (originalRecord) {
|
|
1333
1310
|
originalRecord.alias.push(matcher);
|
|
@@ -1345,20 +1322,27 @@ function createRouterMatcher(routes, globalOptions) {
|
|
|
1345
1322
|
if (isRootAdd && record.name && !isAliasRecord(matcher))
|
|
1346
1323
|
removeRoute(record.name);
|
|
1347
1324
|
}
|
|
1348
|
-
if (
|
|
1325
|
+
if (mainNormalizedRecord.children) {
|
|
1349
1326
|
const children = mainNormalizedRecord.children;
|
|
1350
1327
|
for (let i = 0; i < children.length; i++) {
|
|
1351
1328
|
addRoute(children[i], matcher, originalRecord && originalRecord.children[i]);
|
|
1352
1329
|
}
|
|
1353
1330
|
}
|
|
1354
1331
|
// if there was no original record, then the first one was not an alias and all
|
|
1355
|
-
// other
|
|
1332
|
+
// other aliases (if any) need to reference this record when adding children
|
|
1356
1333
|
originalRecord = originalRecord || matcher;
|
|
1357
1334
|
// TODO: add normalized records for more flexibility
|
|
1358
1335
|
// if (parent && isAliasRecord(originalRecord)) {
|
|
1359
1336
|
// parent.children.push(originalRecord)
|
|
1360
1337
|
// }
|
|
1361
|
-
|
|
1338
|
+
// Avoid adding a record that doesn't display anything. This allows passing through records without a component to
|
|
1339
|
+
// not be reached and pass through the catch all route
|
|
1340
|
+
if ((matcher.record.components &&
|
|
1341
|
+
Object.keys(matcher.record.components).length) ||
|
|
1342
|
+
matcher.record.name ||
|
|
1343
|
+
matcher.record.redirect) {
|
|
1344
|
+
insertMatcher(matcher);
|
|
1345
|
+
}
|
|
1362
1346
|
}
|
|
1363
1347
|
return originalMatcher
|
|
1364
1348
|
? () => {
|
|
@@ -1412,16 +1396,27 @@ function createRouterMatcher(routes, globalOptions) {
|
|
|
1412
1396
|
if ('name' in location && location.name) {
|
|
1413
1397
|
matcher = matcherMap.get(location.name);
|
|
1414
1398
|
if (!matcher)
|
|
1415
|
-
throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
|
|
1399
|
+
throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, {
|
|
1416
1400
|
location,
|
|
1417
1401
|
});
|
|
1402
|
+
// warn if the user is passing invalid params so they can debug it better when they get removed
|
|
1403
|
+
{
|
|
1404
|
+
const invalidParams = Object.keys(location.params || {}).filter(paramName => !matcher.keys.find(k => k.name === paramName));
|
|
1405
|
+
if (invalidParams.length) {
|
|
1406
|
+
warn(`Discarded invalid param(s) "${invalidParams.join('", "')}" when navigating.`);
|
|
1407
|
+
}
|
|
1408
|
+
}
|
|
1418
1409
|
name = matcher.record.name;
|
|
1419
1410
|
params = assign(
|
|
1420
1411
|
// paramsFromLocation is a new object
|
|
1421
1412
|
paramsFromLocation(currentLocation.params,
|
|
1422
1413
|
// only keep params that exist in the resolved location
|
|
1423
1414
|
// TODO: only keep optional params coming from a parent record
|
|
1424
|
-
matcher.keys.filter(k => !k.optional).map(k => k.name)),
|
|
1415
|
+
matcher.keys.filter(k => !k.optional).map(k => k.name)),
|
|
1416
|
+
// discard any existing params in the current location that do not exist here
|
|
1417
|
+
// #1497 this ensures better active/exact matching
|
|
1418
|
+
location.params &&
|
|
1419
|
+
paramsFromLocation(location.params, matcher.keys.map(k => k.name)));
|
|
1425
1420
|
// throws if cannot be stringified
|
|
1426
1421
|
path = matcher.stringify(params);
|
|
1427
1422
|
}
|
|
@@ -1435,7 +1430,6 @@ function createRouterMatcher(routes, globalOptions) {
|
|
|
1435
1430
|
matcher = matchers.find(m => m.re.test(path));
|
|
1436
1431
|
// matcher should have a value after the loop
|
|
1437
1432
|
if (matcher) {
|
|
1438
|
-
// TODO: dev warning of unused params if provided
|
|
1439
1433
|
// we know the matcher works because we tested the regexp
|
|
1440
1434
|
params = matcher.parse(path);
|
|
1441
1435
|
name = matcher.record.name;
|
|
@@ -1448,7 +1442,7 @@ function createRouterMatcher(routes, globalOptions) {
|
|
|
1448
1442
|
? matcherMap.get(currentLocation.name)
|
|
1449
1443
|
: matchers.find(m => m.re.test(currentLocation.path));
|
|
1450
1444
|
if (!matcher)
|
|
1451
|
-
throw createRouterError(1 /* MATCHER_NOT_FOUND */, {
|
|
1445
|
+
throw createRouterError(1 /* ErrorTypes.MATCHER_NOT_FOUND */, {
|
|
1452
1446
|
location,
|
|
1453
1447
|
currentLocation,
|
|
1454
1448
|
});
|
|
@@ -1506,8 +1500,8 @@ function normalizeRouteRecord(record) {
|
|
|
1506
1500
|
updateGuards: new Set(),
|
|
1507
1501
|
enterCallbacks: {},
|
|
1508
1502
|
components: 'components' in record
|
|
1509
|
-
? record.components ||
|
|
1510
|
-
: { default: record.component },
|
|
1503
|
+
? record.components || null
|
|
1504
|
+
: record.component && { default: record.component },
|
|
1511
1505
|
};
|
|
1512
1506
|
}
|
|
1513
1507
|
/**
|
|
@@ -1517,7 +1511,7 @@ function normalizeRouteRecord(record) {
|
|
|
1517
1511
|
*/
|
|
1518
1512
|
function normalizeRecordProps(record) {
|
|
1519
1513
|
const propsObject = {};
|
|
1520
|
-
// props does not exist on redirect records but we can set false directly
|
|
1514
|
+
// props does not exist on redirect records, but we can set false directly
|
|
1521
1515
|
const props = record.props || false;
|
|
1522
1516
|
if ('component' in record) {
|
|
1523
1517
|
propsObject.default = props;
|
|
@@ -1571,17 +1565,31 @@ function isSameParam(a, b) {
|
|
|
1571
1565
|
function checkSameParams(a, b) {
|
|
1572
1566
|
for (const key of a.keys) {
|
|
1573
1567
|
if (!key.optional && !b.keys.find(isSameParam.bind(null, key)))
|
|
1574
|
-
return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}"
|
|
1568
|
+
return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`);
|
|
1575
1569
|
}
|
|
1576
1570
|
for (const key of b.keys) {
|
|
1577
1571
|
if (!key.optional && !a.keys.find(isSameParam.bind(null, key)))
|
|
1578
|
-
return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}"
|
|
1572
|
+
return warn(`Alias "${b.record.path}" and the original record: "${a.record.path}" must have the exact same param named "${key.name}"`);
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* A route with a name and a child with an empty path without a name should warn when adding the route
|
|
1577
|
+
*
|
|
1578
|
+
* @param mainNormalizedRecord - RouteRecordNormalized
|
|
1579
|
+
* @param parent - RouteRecordMatcher
|
|
1580
|
+
*/
|
|
1581
|
+
function checkChildMissingNameWithEmptyPath(mainNormalizedRecord, parent) {
|
|
1582
|
+
if (parent &&
|
|
1583
|
+
parent.record.name &&
|
|
1584
|
+
!mainNormalizedRecord.name &&
|
|
1585
|
+
!mainNormalizedRecord.path) {
|
|
1586
|
+
warn(`The route named "${String(parent.record.name)}" has a child without a name and an empty path. Using that name won't render the empty path child so you probably want to move the name to the child instead. If this is intentional, add a name to the child route to remove the warning.`);
|
|
1579
1587
|
}
|
|
1580
1588
|
}
|
|
1581
1589
|
function checkMissingParamsInAbsolutePath(record, parent) {
|
|
1582
1590
|
for (const key of parent.keys) {
|
|
1583
1591
|
if (!record.keys.find(isSameParam.bind(null, key)))
|
|
1584
|
-
return warn(`Absolute path "${record.record.path}"
|
|
1592
|
+
return warn(`Absolute path "${record.record.path}" must have the exact same param named "${key.name}" as its parent "${parent.record.path}".`);
|
|
1585
1593
|
}
|
|
1586
1594
|
}
|
|
1587
1595
|
function isRecordChildOf(record, parent) {
|
|
@@ -1595,7 +1603,7 @@ function isRecordChildOf(record, parent) {
|
|
|
1595
1603
|
* On top of that, the RFC3986 (https://tools.ietf.org/html/rfc3986#section-2.2)
|
|
1596
1604
|
* defines some extra characters to be encoded. Most browsers do not encode them
|
|
1597
1605
|
* in encodeURI https://github.com/whatwg/url/issues/369, so it may be safer to
|
|
1598
|
-
* also encode `!'()*`. Leaving
|
|
1606
|
+
* also encode `!'()*`. Leaving un-encoded only ASCII alphanumeric(`a-zA-Z0-9`)
|
|
1599
1607
|
* plus `-._~`. This extra safety should be applied to query by patching the
|
|
1600
1608
|
* string returned by encodeURIComponent encodeURI also encodes `[\]^`. `\`
|
|
1601
1609
|
* should be encoded to avoid ambiguity. Browsers (IE, FF, C) transform a `\`
|
|
@@ -1619,7 +1627,7 @@ const PLUS_RE = /\+/g; // %2B
|
|
|
1619
1627
|
* application/x-www-form-urlencoded
|
|
1620
1628
|
* (https://url.spec.whatwg.org/#urlencoded-parsing) and most browsers seems lo
|
|
1621
1629
|
* leave the plus character as is in queries. To be more flexible, we allow the
|
|
1622
|
-
* plus character on the query but it can also be manually encoded by the user.
|
|
1630
|
+
* plus character on the query, but it can also be manually encoded by the user.
|
|
1623
1631
|
*
|
|
1624
1632
|
* Resources:
|
|
1625
1633
|
* - https://url.spec.whatwg.org/#urlencoded-parsing
|
|
@@ -1751,7 +1759,7 @@ function parseQuery(search) {
|
|
|
1751
1759
|
if (key in query) {
|
|
1752
1760
|
// an extra variable for ts types
|
|
1753
1761
|
let currentValue = query[key];
|
|
1754
|
-
if (!
|
|
1762
|
+
if (!isArray(currentValue)) {
|
|
1755
1763
|
currentValue = query[key] = [currentValue];
|
|
1756
1764
|
}
|
|
1757
1765
|
currentValue.push(value);
|
|
@@ -1784,7 +1792,7 @@ function stringifyQuery(query) {
|
|
|
1784
1792
|
continue;
|
|
1785
1793
|
}
|
|
1786
1794
|
// keep null values
|
|
1787
|
-
const values =
|
|
1795
|
+
const values = isArray(value)
|
|
1788
1796
|
? value.map(v => v && encodeQueryValue(v))
|
|
1789
1797
|
: [value && encodeQueryValue(value)];
|
|
1790
1798
|
values.forEach(value => {
|
|
@@ -1813,7 +1821,7 @@ function normalizeQuery(query) {
|
|
|
1813
1821
|
for (const key in query) {
|
|
1814
1822
|
const value = query[key];
|
|
1815
1823
|
if (value !== undefined) {
|
|
1816
|
-
normalizedQuery[key] =
|
|
1824
|
+
normalizedQuery[key] = isArray(value)
|
|
1817
1825
|
? value.map(v => (v == null ? null : '' + v))
|
|
1818
1826
|
: value == null
|
|
1819
1827
|
? value
|
|
@@ -1823,6 +1831,43 @@ function normalizeQuery(query) {
|
|
|
1823
1831
|
return normalizedQuery;
|
|
1824
1832
|
}
|
|
1825
1833
|
|
|
1834
|
+
/**
|
|
1835
|
+
* RouteRecord being rendered by the closest ancestor Router View. Used for
|
|
1836
|
+
* `onBeforeRouteUpdate` and `onBeforeRouteLeave`. rvlm stands for Router View
|
|
1837
|
+
* Location Matched
|
|
1838
|
+
*
|
|
1839
|
+
* @internal
|
|
1840
|
+
*/
|
|
1841
|
+
const matchedRouteKey = Symbol('router view location matched' );
|
|
1842
|
+
/**
|
|
1843
|
+
* Allows overriding the router view depth to control which component in
|
|
1844
|
+
* `matched` is rendered. rvd stands for Router View Depth
|
|
1845
|
+
*
|
|
1846
|
+
* @internal
|
|
1847
|
+
*/
|
|
1848
|
+
const viewDepthKey = Symbol('router view depth' );
|
|
1849
|
+
/**
|
|
1850
|
+
* Allows overriding the router instance returned by `useRouter` in tests. r
|
|
1851
|
+
* stands for router
|
|
1852
|
+
*
|
|
1853
|
+
* @internal
|
|
1854
|
+
*/
|
|
1855
|
+
const routerKey = Symbol('router' );
|
|
1856
|
+
/**
|
|
1857
|
+
* Allows overriding the current route returned by `useRoute` in tests. rl
|
|
1858
|
+
* stands for route location
|
|
1859
|
+
*
|
|
1860
|
+
* @internal
|
|
1861
|
+
*/
|
|
1862
|
+
const routeLocationKey = Symbol('route location' );
|
|
1863
|
+
/**
|
|
1864
|
+
* Allows overriding the current route used by router-view. Internally this is
|
|
1865
|
+
* used when the `route` prop is passed.
|
|
1866
|
+
*
|
|
1867
|
+
* @internal
|
|
1868
|
+
*/
|
|
1869
|
+
const routerViewLocationKey = Symbol('router view location' );
|
|
1870
|
+
|
|
1826
1871
|
/**
|
|
1827
1872
|
* Create a list of callbacks that can be reset. Used to create before and after navigation guards list
|
|
1828
1873
|
*/
|
|
@@ -1873,7 +1918,7 @@ function onBeforeRouteLeave(leaveGuard) {
|
|
|
1873
1918
|
// to avoid warning
|
|
1874
1919
|
{}).value;
|
|
1875
1920
|
if (!activeRecord) {
|
|
1876
|
-
warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside
|
|
1921
|
+
warn('No active route record was found when calling `onBeforeRouteLeave()`. Make sure you call this function inside a component child of <router-view>. Maybe you called it inside of App.kdu?');
|
|
1877
1922
|
return;
|
|
1878
1923
|
}
|
|
1879
1924
|
registerGuard(activeRecord, 'leaveGuards', leaveGuard);
|
|
@@ -1894,7 +1939,7 @@ function onBeforeRouteUpdate(updateGuard) {
|
|
|
1894
1939
|
// to avoid warning
|
|
1895
1940
|
{}).value;
|
|
1896
1941
|
if (!activeRecord) {
|
|
1897
|
-
warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside
|
|
1942
|
+
warn('No active route record was found when calling `onBeforeRouteUpdate()`. Make sure you call this function inside a component child of <router-view>. Maybe you called it inside of App.kdu?');
|
|
1898
1943
|
return;
|
|
1899
1944
|
}
|
|
1900
1945
|
registerGuard(activeRecord, 'updateGuards', updateGuard);
|
|
@@ -1906,16 +1951,17 @@ function guardToPromiseFn(guard, to, from, record, name) {
|
|
|
1906
1951
|
(record.enterCallbacks[name] = record.enterCallbacks[name] || []);
|
|
1907
1952
|
return () => new Promise((resolve, reject) => {
|
|
1908
1953
|
const next = (valid) => {
|
|
1909
|
-
if (valid === false)
|
|
1910
|
-
reject(createRouterError(4 /* NAVIGATION_ABORTED */, {
|
|
1954
|
+
if (valid === false) {
|
|
1955
|
+
reject(createRouterError(4 /* ErrorTypes.NAVIGATION_ABORTED */, {
|
|
1911
1956
|
from,
|
|
1912
1957
|
to,
|
|
1913
1958
|
}));
|
|
1959
|
+
}
|
|
1914
1960
|
else if (valid instanceof Error) {
|
|
1915
1961
|
reject(valid);
|
|
1916
1962
|
}
|
|
1917
1963
|
else if (isRouteLocation(valid)) {
|
|
1918
|
-
reject(createRouterError(2 /* NAVIGATION_GUARD_REDIRECT */, {
|
|
1964
|
+
reject(createRouterError(2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */, {
|
|
1919
1965
|
from: to,
|
|
1920
1966
|
to: valid,
|
|
1921
1967
|
}));
|
|
@@ -1924,8 +1970,9 @@ function guardToPromiseFn(guard, to, from, record, name) {
|
|
|
1924
1970
|
if (enterCallbackArray &&
|
|
1925
1971
|
// since enterCallbackArray is truthy, both record and name also are
|
|
1926
1972
|
record.enterCallbacks[name] === enterCallbackArray &&
|
|
1927
|
-
typeof valid === 'function')
|
|
1973
|
+
typeof valid === 'function') {
|
|
1928
1974
|
enterCallbackArray.push(valid);
|
|
1975
|
+
}
|
|
1929
1976
|
resolve();
|
|
1930
1977
|
}
|
|
1931
1978
|
};
|
|
@@ -1945,7 +1992,6 @@ function guardToPromiseFn(guard, to, from, record, name) {
|
|
|
1945
1992
|
}
|
|
1946
1993
|
return resolvedValue;
|
|
1947
1994
|
});
|
|
1948
|
-
// TODO: test me!
|
|
1949
1995
|
}
|
|
1950
1996
|
else if (guardReturn !== undefined) {
|
|
1951
1997
|
// @ts-expect-error: _called is added at canOnlyBeCalledOnce
|
|
@@ -1973,6 +2019,10 @@ function canOnlyBeCalledOnce(next, to, from) {
|
|
|
1973
2019
|
function extractComponentsGuards(matched, guardType, to, from) {
|
|
1974
2020
|
const guards = [];
|
|
1975
2021
|
for (const record of matched) {
|
|
2022
|
+
if (!record.components && !record.children.length) {
|
|
2023
|
+
warn(`Record with path "${record.path}" is either missing a "component(s)"` +
|
|
2024
|
+
` or "children" property.`);
|
|
2025
|
+
}
|
|
1976
2026
|
for (const name in record.components) {
|
|
1977
2027
|
let rawComponent = record.components[name];
|
|
1978
2028
|
{
|
|
@@ -2029,6 +2079,7 @@ function extractComponentsGuards(matched, guardType, to, from) {
|
|
|
2029
2079
|
? resolved.default
|
|
2030
2080
|
: resolved;
|
|
2031
2081
|
// replace the function with the resolved component
|
|
2082
|
+
// cannot be null or undefined because we went into the for loop
|
|
2032
2083
|
record.components[name] = resolvedComponent;
|
|
2033
2084
|
// __kccOpts is added by kdu-class-component and contain the regular options
|
|
2034
2085
|
const options = resolvedComponent.__kccOpts || resolvedComponent;
|
|
@@ -2042,6 +2093,7 @@ function extractComponentsGuards(matched, guardType, to, from) {
|
|
|
2042
2093
|
}
|
|
2043
2094
|
/**
|
|
2044
2095
|
* Allows differentiating lazy components from functional components and kdu-class-component
|
|
2096
|
+
* @internal
|
|
2045
2097
|
*
|
|
2046
2098
|
* @param component
|
|
2047
2099
|
*/
|
|
@@ -2050,6 +2102,34 @@ function isRouteComponent(component) {
|
|
|
2050
2102
|
'displayName' in component ||
|
|
2051
2103
|
'props' in component ||
|
|
2052
2104
|
'__kccOpts' in component);
|
|
2105
|
+
}
|
|
2106
|
+
/**
|
|
2107
|
+
* Ensures a route is loaded, so it can be passed as o prop to `<RouterView>`.
|
|
2108
|
+
*
|
|
2109
|
+
* @param route - resolved route to load
|
|
2110
|
+
*/
|
|
2111
|
+
function loadRouteLocation(route) {
|
|
2112
|
+
return route.matched.every(record => record.redirect)
|
|
2113
|
+
? Promise.reject(new Error('Cannot load a route that redirects.'))
|
|
2114
|
+
: Promise.all(route.matched.map(record => record.components &&
|
|
2115
|
+
Promise.all(Object.keys(record.components).reduce((promises, name) => {
|
|
2116
|
+
const rawComponent = record.components[name];
|
|
2117
|
+
if (typeof rawComponent === 'function' &&
|
|
2118
|
+
!('displayName' in rawComponent)) {
|
|
2119
|
+
promises.push(rawComponent().then(resolved => {
|
|
2120
|
+
if (!resolved)
|
|
2121
|
+
return Promise.reject(new Error(`Couldn't resolve component "${name}" at "${record.path}". Ensure you passed a function that returns a promise.`));
|
|
2122
|
+
const resolvedComponent = isESModule(resolved)
|
|
2123
|
+
? resolved.default
|
|
2124
|
+
: resolved;
|
|
2125
|
+
// replace the function with the resolved component
|
|
2126
|
+
// cannot be null or undefined because we went into the for loop
|
|
2127
|
+
record.components[name] = resolvedComponent;
|
|
2128
|
+
return;
|
|
2129
|
+
}));
|
|
2130
|
+
}
|
|
2131
|
+
return promises;
|
|
2132
|
+
}, [])))).then(() => route);
|
|
2053
2133
|
}
|
|
2054
2134
|
|
|
2055
2135
|
// TODO: we could allow currentRoute as a prop to expose `isActive` and
|
|
@@ -2115,6 +2195,9 @@ function useLink(props) {
|
|
|
2115
2195
|
}, { flush: 'post' });
|
|
2116
2196
|
}
|
|
2117
2197
|
}
|
|
2198
|
+
/**
|
|
2199
|
+
* NOTE: update {@link _RouterLinkI}'s `$slots` type when updating this
|
|
2200
|
+
*/
|
|
2118
2201
|
return {
|
|
2119
2202
|
route,
|
|
2120
2203
|
href: computed(() => route.value.href),
|
|
@@ -2164,7 +2247,7 @@ const RouterLinkImpl = /*#__PURE__*/ defineComponent({
|
|
|
2164
2247
|
: null,
|
|
2165
2248
|
href: link.href,
|
|
2166
2249
|
// this would override user added attrs but Kdu will still add
|
|
2167
|
-
// the listener so we end up triggering both
|
|
2250
|
+
// the listener, so we end up triggering both
|
|
2168
2251
|
onClick: link.navigate,
|
|
2169
2252
|
class: elClass.value,
|
|
2170
2253
|
}, children);
|
|
@@ -2209,7 +2292,7 @@ function includesParams(outer, inner) {
|
|
|
2209
2292
|
return false;
|
|
2210
2293
|
}
|
|
2211
2294
|
else {
|
|
2212
|
-
if (!
|
|
2295
|
+
if (!isArray(outerValue) ||
|
|
2213
2296
|
outerValue.length !== innerValue.length ||
|
|
2214
2297
|
innerValue.some((value, i) => value !== outerValue[i]))
|
|
2215
2298
|
return false;
|
|
@@ -2253,9 +2336,21 @@ const RouterViewImpl = /*#__PURE__*/ defineComponent({
|
|
|
2253
2336
|
warnDeprecatedUsage();
|
|
2254
2337
|
const injectedRoute = inject(routerViewLocationKey);
|
|
2255
2338
|
const routeToDisplay = computed(() => props.route || injectedRoute.value);
|
|
2256
|
-
const
|
|
2257
|
-
|
|
2258
|
-
|
|
2339
|
+
const injectedDepth = inject(viewDepthKey, 0);
|
|
2340
|
+
// The depth changes based on empty components option, which allows passthrough routes e.g. routes with children
|
|
2341
|
+
// that are used to reuse the `path` property
|
|
2342
|
+
const depth = computed(() => {
|
|
2343
|
+
let initialDepth = unref(injectedDepth);
|
|
2344
|
+
const { matched } = routeToDisplay.value;
|
|
2345
|
+
let matchedRoute;
|
|
2346
|
+
while ((matchedRoute = matched[initialDepth]) &&
|
|
2347
|
+
!matchedRoute.components) {
|
|
2348
|
+
initialDepth++;
|
|
2349
|
+
}
|
|
2350
|
+
return initialDepth;
|
|
2351
|
+
});
|
|
2352
|
+
const matchedRouteRef = computed(() => routeToDisplay.value.matched[depth.value]);
|
|
2353
|
+
provide(viewDepthKey, computed(() => depth.value + 1));
|
|
2259
2354
|
provide(matchedRouteKey, matchedRouteRef);
|
|
2260
2355
|
provide(routerViewLocationKey, routeToDisplay);
|
|
2261
2356
|
const viewRef = ref();
|
|
@@ -2267,7 +2362,7 @@ const RouterViewImpl = /*#__PURE__*/ defineComponent({
|
|
|
2267
2362
|
// this will update the instance for new instances as well as reused
|
|
2268
2363
|
// instances when navigating to a new route
|
|
2269
2364
|
to.instances[name] = instance;
|
|
2270
|
-
// the component instance is reused for a different route or name so
|
|
2365
|
+
// the component instance is reused for a different route or name, so
|
|
2271
2366
|
// we copy any saved update or leave guards. With async setup, the
|
|
2272
2367
|
// mounting component will mount before the matchedRoute changes,
|
|
2273
2368
|
// making instance === oldInstance, so we check if guards have been
|
|
@@ -2293,16 +2388,16 @@ const RouterViewImpl = /*#__PURE__*/ defineComponent({
|
|
|
2293
2388
|
}, { flush: 'post' });
|
|
2294
2389
|
return () => {
|
|
2295
2390
|
const route = routeToDisplay.value;
|
|
2296
|
-
const matchedRoute = matchedRouteRef.value;
|
|
2297
|
-
const ViewComponent = matchedRoute && matchedRoute.components[props.name];
|
|
2298
2391
|
// we need the value at the time we render because when we unmount, we
|
|
2299
2392
|
// navigated to a different location so the value is different
|
|
2300
2393
|
const currentName = props.name;
|
|
2394
|
+
const matchedRoute = matchedRouteRef.value;
|
|
2395
|
+
const ViewComponent = matchedRoute && matchedRoute.components[currentName];
|
|
2301
2396
|
if (!ViewComponent) {
|
|
2302
2397
|
return normalizeSlot(slots.default, { Component: ViewComponent, route });
|
|
2303
2398
|
}
|
|
2304
2399
|
// props from route configuration
|
|
2305
|
-
const routePropsOption = matchedRoute.props[
|
|
2400
|
+
const routePropsOption = matchedRoute.props[currentName];
|
|
2306
2401
|
const routeProps = routePropsOption
|
|
2307
2402
|
? routePropsOption === true
|
|
2308
2403
|
? route.params
|
|
@@ -2324,12 +2419,12 @@ const RouterViewImpl = /*#__PURE__*/ defineComponent({
|
|
|
2324
2419
|
component.ref) {
|
|
2325
2420
|
// TODO: can display if it's an alias, its props
|
|
2326
2421
|
const info = {
|
|
2327
|
-
depth,
|
|
2422
|
+
depth: depth.value,
|
|
2328
2423
|
name: matchedRoute.name,
|
|
2329
2424
|
path: matchedRoute.path,
|
|
2330
2425
|
meta: matchedRoute.meta,
|
|
2331
2426
|
};
|
|
2332
|
-
const internalInstances =
|
|
2427
|
+
const internalInstances = isArray(component.ref)
|
|
2333
2428
|
? component.ref.map(r => r.i)
|
|
2334
2429
|
: [component.ref.i];
|
|
2335
2430
|
internalInstances.forEach(instance => {
|
|
@@ -2375,6 +2470,13 @@ function warnDeprecatedUsage() {
|
|
|
2375
2470
|
}
|
|
2376
2471
|
}
|
|
2377
2472
|
|
|
2473
|
+
/**
|
|
2474
|
+
* Copies a route location and removes any problematic properties that cannot be shown in devtools (e.g. Kdu instances).
|
|
2475
|
+
*
|
|
2476
|
+
* @param routeLocation - routeLocation to format
|
|
2477
|
+
* @param tooltip - optional tooltip
|
|
2478
|
+
* @returns a copy of the routeLocation
|
|
2479
|
+
*/
|
|
2378
2480
|
function formatRouteLocation(routeLocation, tooltip) {
|
|
2379
2481
|
const copy = assign({}, routeLocation, {
|
|
2380
2482
|
// remove variables that can contain kdu instances
|
|
@@ -2416,6 +2518,9 @@ function addDevtools(app, router, matcher) {
|
|
|
2416
2518
|
componentStateTypes: ['Routing'],
|
|
2417
2519
|
app,
|
|
2418
2520
|
}, api => {
|
|
2521
|
+
if (typeof api.now !== 'function') {
|
|
2522
|
+
console.warn('[Kdu Router]: You seem to be using an outdated version of Kdu Devtools. Are you still using the Beta release instead of the stable one? You can find the links at https://kdujs-devtools.web.app/guide/installation.html.');
|
|
2523
|
+
}
|
|
2419
2524
|
// display state added by the router
|
|
2420
2525
|
api.on.inspectComponent((payload, ctx) => {
|
|
2421
2526
|
if (payload.instanceData) {
|
|
@@ -2439,7 +2544,7 @@ function addDevtools(app, router, matcher) {
|
|
|
2439
2544
|
});
|
|
2440
2545
|
}
|
|
2441
2546
|
// if multiple useLink are used
|
|
2442
|
-
if (
|
|
2547
|
+
if (isArray(componentInstance.__krl_devtools)) {
|
|
2443
2548
|
componentInstance.__devtoolsApi = api;
|
|
2444
2549
|
componentInstance.__krl_devtools.forEach(devtoolsData => {
|
|
2445
2550
|
let backgroundColor = ORANGE_400;
|
|
@@ -2487,7 +2592,6 @@ function addDevtools(app, router, matcher) {
|
|
|
2487
2592
|
title: 'Error during Navigation',
|
|
2488
2593
|
subtitle: to.fullPath,
|
|
2489
2594
|
logType: 'error',
|
|
2490
|
-
// @ts-ignore
|
|
2491
2595
|
time: api.now(),
|
|
2492
2596
|
data: { error },
|
|
2493
2597
|
groupId: to.meta.__navigationId,
|
|
@@ -2509,7 +2613,6 @@ function addDevtools(app, router, matcher) {
|
|
|
2509
2613
|
api.addTimelineEvent({
|
|
2510
2614
|
layerId: navigationsLayerId,
|
|
2511
2615
|
event: {
|
|
2512
|
-
// @ts-ignore
|
|
2513
2616
|
time: api.now(),
|
|
2514
2617
|
title: 'Start of navigation',
|
|
2515
2618
|
subtitle: to.fullPath,
|
|
@@ -2545,7 +2648,6 @@ function addDevtools(app, router, matcher) {
|
|
|
2545
2648
|
event: {
|
|
2546
2649
|
title: 'End of navigation',
|
|
2547
2650
|
subtitle: to.fullPath,
|
|
2548
|
-
// @ts-ignore
|
|
2549
2651
|
time: api.now(),
|
|
2550
2652
|
data,
|
|
2551
2653
|
logType: failure ? 'warning' : 'default',
|
|
@@ -2595,7 +2697,7 @@ function addDevtools(app, router, matcher) {
|
|
|
2595
2697
|
api.on.getInspectorState(payload => {
|
|
2596
2698
|
if (payload.app === app && payload.inspectorId === routerInspectorId) {
|
|
2597
2699
|
const routes = matcher.getRoutes();
|
|
2598
|
-
const route = routes.find(route => route.record.
|
|
2700
|
+
const route = routes.find(route => route.record.__kd_id === payload.nodeId);
|
|
2599
2701
|
if (route) {
|
|
2600
2702
|
payload.state = {
|
|
2601
2703
|
options: formatRouteRecordMatcherForStateInspector(route),
|
|
@@ -2659,6 +2761,13 @@ function formatRouteRecordMatcherForStateInspector(route) {
|
|
|
2659
2761
|
value: route.alias.map(alias => alias.record.path),
|
|
2660
2762
|
});
|
|
2661
2763
|
}
|
|
2764
|
+
if (Object.keys(route.record.meta).length) {
|
|
2765
|
+
fields.push({
|
|
2766
|
+
editable: false,
|
|
2767
|
+
key: 'meta',
|
|
2768
|
+
value: route.record.meta,
|
|
2769
|
+
});
|
|
2770
|
+
}
|
|
2662
2771
|
fields.push({
|
|
2663
2772
|
key: 'score',
|
|
2664
2773
|
editable: false,
|
|
@@ -2701,21 +2810,21 @@ function formatRouteRecordForInspector(route) {
|
|
|
2701
2810
|
backgroundColor: ORANGE_400,
|
|
2702
2811
|
});
|
|
2703
2812
|
}
|
|
2704
|
-
if (route.
|
|
2813
|
+
if (route.__kd_match) {
|
|
2705
2814
|
tags.push({
|
|
2706
2815
|
label: 'matches',
|
|
2707
2816
|
textColor: 0,
|
|
2708
2817
|
backgroundColor: PINK_500,
|
|
2709
2818
|
});
|
|
2710
2819
|
}
|
|
2711
|
-
if (route.
|
|
2820
|
+
if (route.__kd_exactActive) {
|
|
2712
2821
|
tags.push({
|
|
2713
2822
|
label: 'exact',
|
|
2714
2823
|
textColor: 0,
|
|
2715
2824
|
backgroundColor: LIME_500,
|
|
2716
2825
|
});
|
|
2717
2826
|
}
|
|
2718
|
-
if (route.
|
|
2827
|
+
if (route.__kd_active) {
|
|
2719
2828
|
tags.push({
|
|
2720
2829
|
label: 'active',
|
|
2721
2830
|
textColor: 0,
|
|
@@ -2724,18 +2833,19 @@ function formatRouteRecordForInspector(route) {
|
|
|
2724
2833
|
}
|
|
2725
2834
|
if (record.redirect) {
|
|
2726
2835
|
tags.push({
|
|
2727
|
-
label:
|
|
2728
|
-
|
|
2836
|
+
label: typeof record.redirect === 'string'
|
|
2837
|
+
? `redirect: ${record.redirect}`
|
|
2838
|
+
: 'redirects',
|
|
2729
2839
|
textColor: 0xffffff,
|
|
2730
2840
|
backgroundColor: DARK,
|
|
2731
2841
|
});
|
|
2732
2842
|
}
|
|
2733
2843
|
// add an id to be able to select it. Using the `path` is not possible because
|
|
2734
2844
|
// empty path children would collide with their parents
|
|
2735
|
-
let id = record.
|
|
2845
|
+
let id = record.__kd_id;
|
|
2736
2846
|
if (id == null) {
|
|
2737
2847
|
id = String(routeRecordId++);
|
|
2738
|
-
record.
|
|
2848
|
+
record.__kd_id = id;
|
|
2739
2849
|
}
|
|
2740
2850
|
return {
|
|
2741
2851
|
id,
|
|
@@ -2752,19 +2862,19 @@ function markRouteRecordActive(route, currentRoute) {
|
|
|
2752
2862
|
// reset the matching state
|
|
2753
2863
|
const isExactActive = currentRoute.matched.length &&
|
|
2754
2864
|
isSameRouteRecord(currentRoute.matched[currentRoute.matched.length - 1], route.record);
|
|
2755
|
-
route.
|
|
2865
|
+
route.__kd_exactActive = route.__kd_active = isExactActive;
|
|
2756
2866
|
if (!isExactActive) {
|
|
2757
|
-
route.
|
|
2867
|
+
route.__kd_active = currentRoute.matched.some(match => isSameRouteRecord(match, route.record));
|
|
2758
2868
|
}
|
|
2759
2869
|
route.children.forEach(childRoute => markRouteRecordActive(childRoute, currentRoute));
|
|
2760
2870
|
}
|
|
2761
2871
|
function resetMatchStateOnRouteRecord(route) {
|
|
2762
|
-
route.
|
|
2872
|
+
route.__kd_match = false;
|
|
2763
2873
|
route.children.forEach(resetMatchStateOnRouteRecord);
|
|
2764
2874
|
}
|
|
2765
2875
|
function isRouteMatching(route, filter) {
|
|
2766
2876
|
const found = String(route.re).match(EXTRACT_REGEXP_RE);
|
|
2767
|
-
route.
|
|
2877
|
+
route.__kd_match = false;
|
|
2768
2878
|
if (!found || found.length < 3) {
|
|
2769
2879
|
return false;
|
|
2770
2880
|
}
|
|
@@ -2775,7 +2885,7 @@ function isRouteMatching(route, filter) {
|
|
|
2775
2885
|
route.children.forEach(child => isRouteMatching(child, filter));
|
|
2776
2886
|
// exception case: `/`
|
|
2777
2887
|
if (route.record.path !== '/' || filter === '/') {
|
|
2778
|
-
route.
|
|
2888
|
+
route.__kd_match = route.re.test(filter);
|
|
2779
2889
|
return true;
|
|
2780
2890
|
}
|
|
2781
2891
|
// hide the / route
|
|
@@ -2904,7 +3014,7 @@ function createRouter(options) {
|
|
|
2904
3014
|
delete targetParams[key];
|
|
2905
3015
|
}
|
|
2906
3016
|
}
|
|
2907
|
-
// pass encoded values to the matcher so it can produce encoded path and fullPath
|
|
3017
|
+
// pass encoded values to the matcher, so it can produce encoded path and fullPath
|
|
2908
3018
|
matcherLocation = assign({}, rawLocation, {
|
|
2909
3019
|
params: encodeParams(rawLocation.params),
|
|
2910
3020
|
});
|
|
@@ -2917,7 +3027,7 @@ function createRouter(options) {
|
|
|
2917
3027
|
if (hash && !hash.startsWith('#')) {
|
|
2918
3028
|
warn(`A \`hash\` should always start with the character "#". Replace "${hash}" with "#${hash}".`);
|
|
2919
3029
|
}
|
|
2920
|
-
//
|
|
3030
|
+
// the matcher might have merged current location params, so
|
|
2921
3031
|
// we need to run the decoding again
|
|
2922
3032
|
matchedRoute.params = normalizeParams(decodeParams(matchedRoute.params));
|
|
2923
3033
|
const fullPath = stringifyURL(stringifyQuery$1, assign({}, rawLocation, {
|
|
@@ -2958,7 +3068,7 @@ function createRouter(options) {
|
|
|
2958
3068
|
}
|
|
2959
3069
|
function checkCanceledNavigation(to, from) {
|
|
2960
3070
|
if (pendingLocation !== to) {
|
|
2961
|
-
return createRouterError(8 /* NAVIGATION_CANCELLED */, {
|
|
3071
|
+
return createRouterError(8 /* ErrorTypes.NAVIGATION_CANCELLED */, {
|
|
2962
3072
|
from,
|
|
2963
3073
|
to,
|
|
2964
3074
|
});
|
|
@@ -2993,7 +3103,8 @@ function createRouter(options) {
|
|
|
2993
3103
|
return assign({
|
|
2994
3104
|
query: to.query,
|
|
2995
3105
|
hash: to.hash,
|
|
2996
|
-
params
|
|
3106
|
+
// avoid transferring params if the redirect has a path
|
|
3107
|
+
params: 'path' in newTargetLocation ? {} : to.params,
|
|
2997
3108
|
}, newTargetLocation);
|
|
2998
3109
|
}
|
|
2999
3110
|
}
|
|
@@ -3007,7 +3118,9 @@ function createRouter(options) {
|
|
|
3007
3118
|
const shouldRedirect = handleRedirectRecord(targetLocation);
|
|
3008
3119
|
if (shouldRedirect)
|
|
3009
3120
|
return pushWithRedirect(assign(locationAsObject(shouldRedirect), {
|
|
3010
|
-
state:
|
|
3121
|
+
state: typeof shouldRedirect === 'object'
|
|
3122
|
+
? assign({}, data, shouldRedirect.state)
|
|
3123
|
+
: data,
|
|
3011
3124
|
force,
|
|
3012
3125
|
replace,
|
|
3013
3126
|
}),
|
|
@@ -3018,7 +3131,7 @@ function createRouter(options) {
|
|
|
3018
3131
|
toLocation.redirectedFrom = redirectedFrom;
|
|
3019
3132
|
let failure;
|
|
3020
3133
|
if (!force && isSameRouteLocation(stringifyQuery$1, from, targetLocation)) {
|
|
3021
|
-
failure = createRouterError(16 /* NAVIGATION_DUPLICATED */, { to: toLocation, from });
|
|
3134
|
+
failure = createRouterError(16 /* ErrorTypes.NAVIGATION_DUPLICATED */, { to: toLocation, from });
|
|
3022
3135
|
// trigger scroll to allow scrolling to the same anchor
|
|
3023
3136
|
handleScroll(from, from,
|
|
3024
3137
|
// this is a push, the only way for it to be triggered from a
|
|
@@ -3031,14 +3144,14 @@ function createRouter(options) {
|
|
|
3031
3144
|
return (failure ? Promise.resolve(failure) : navigate(toLocation, from))
|
|
3032
3145
|
.catch((error) => isNavigationFailure(error)
|
|
3033
3146
|
? // navigation redirects still mark the router as ready
|
|
3034
|
-
isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */)
|
|
3147
|
+
isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)
|
|
3035
3148
|
? error
|
|
3036
3149
|
: markAsReady(error) // also returns the error
|
|
3037
3150
|
: // reject any unknown error
|
|
3038
3151
|
triggerError(error, toLocation, from))
|
|
3039
3152
|
.then((failure) => {
|
|
3040
3153
|
if (failure) {
|
|
3041
|
-
if (isNavigationFailure(failure, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
|
|
3154
|
+
if (isNavigationFailure(failure, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) {
|
|
3042
3155
|
if (// we are redirecting to the same location we were already at
|
|
3043
3156
|
isSameRouteLocation(stringifyQuery$1, resolve(failure.to), toLocation) &&
|
|
3044
3157
|
// and we have done it a couple of times
|
|
@@ -3053,10 +3166,14 @@ function createRouter(options) {
|
|
|
3053
3166
|
}
|
|
3054
3167
|
return pushWithRedirect(
|
|
3055
3168
|
// keep options
|
|
3056
|
-
assign(
|
|
3057
|
-
|
|
3058
|
-
force,
|
|
3169
|
+
assign({
|
|
3170
|
+
// preserve an existing replacement but allow the redirect to override it
|
|
3059
3171
|
replace,
|
|
3172
|
+
}, locationAsObject(failure.to), {
|
|
3173
|
+
state: typeof failure.to === 'object'
|
|
3174
|
+
? assign({}, data, failure.to.state)
|
|
3175
|
+
: data,
|
|
3176
|
+
force,
|
|
3060
3177
|
}),
|
|
3061
3178
|
// preserve the original redirectedFrom if any
|
|
3062
3179
|
redirectedFrom || toLocation);
|
|
@@ -3122,7 +3239,7 @@ function createRouter(options) {
|
|
|
3122
3239
|
for (const record of to.matched) {
|
|
3123
3240
|
// do not trigger beforeEnter on reused views
|
|
3124
3241
|
if (record.beforeEnter && !from.matched.includes(record)) {
|
|
3125
|
-
if (
|
|
3242
|
+
if (isArray(record.beforeEnter)) {
|
|
3126
3243
|
for (const beforeEnter of record.beforeEnter)
|
|
3127
3244
|
guards.push(guardToPromiseFn(beforeEnter, to, from));
|
|
3128
3245
|
}
|
|
@@ -3155,7 +3272,7 @@ function createRouter(options) {
|
|
|
3155
3272
|
return runGuardQueue(guards);
|
|
3156
3273
|
})
|
|
3157
3274
|
// catch any navigation canceled
|
|
3158
|
-
.catch(err => isNavigationFailure(err, 8 /* NAVIGATION_CANCELLED */)
|
|
3275
|
+
.catch(err => isNavigationFailure(err, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)
|
|
3159
3276
|
? err
|
|
3160
3277
|
: Promise.reject(err)));
|
|
3161
3278
|
}
|
|
@@ -3202,6 +3319,8 @@ function createRouter(options) {
|
|
|
3202
3319
|
if (removeHistoryListener)
|
|
3203
3320
|
return;
|
|
3204
3321
|
removeHistoryListener = routerHistory.listen((to, _from, info) => {
|
|
3322
|
+
if (!router.listening)
|
|
3323
|
+
return;
|
|
3205
3324
|
// cannot be a redirect route because it was in history
|
|
3206
3325
|
const toLocation = resolve(to);
|
|
3207
3326
|
// due to dynamic routing, and to hash history with manual navigation
|
|
@@ -3220,15 +3339,15 @@ function createRouter(options) {
|
|
|
3220
3339
|
}
|
|
3221
3340
|
navigate(toLocation, from)
|
|
3222
3341
|
.catch((error) => {
|
|
3223
|
-
if (isNavigationFailure(error, 4 /* NAVIGATION_ABORTED */ | 8 /* NAVIGATION_CANCELLED */)) {
|
|
3342
|
+
if (isNavigationFailure(error, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) {
|
|
3224
3343
|
return error;
|
|
3225
3344
|
}
|
|
3226
|
-
if (isNavigationFailure(error, 2 /* NAVIGATION_GUARD_REDIRECT */)) {
|
|
3345
|
+
if (isNavigationFailure(error, 2 /* ErrorTypes.NAVIGATION_GUARD_REDIRECT */)) {
|
|
3227
3346
|
// Here we could call if (info.delta) routerHistory.go(-info.delta,
|
|
3228
3347
|
// false) but this is bug prone as we have no way to wait the
|
|
3229
3348
|
// navigation to be finished before calling pushWithRedirect. Using
|
|
3230
|
-
// a setTimeout of 16ms seems to work but there is
|
|
3231
|
-
// it to work on every browser. So
|
|
3349
|
+
// a setTimeout of 16ms seems to work but there is no guarantee for
|
|
3350
|
+
// it to work on every browser. So instead we do not restore the
|
|
3232
3351
|
// history entry and trigger a new navigation as requested by the
|
|
3233
3352
|
// navigation guard.
|
|
3234
3353
|
// the error is already handled by router.push we just want to avoid
|
|
@@ -3238,10 +3357,10 @@ function createRouter(options) {
|
|
|
3238
3357
|
)
|
|
3239
3358
|
.then(failure => {
|
|
3240
3359
|
// manual change in hash history #916 ending up in the URL not
|
|
3241
|
-
// changing but it was changed by the manual url change, so we
|
|
3360
|
+
// changing, but it was changed by the manual url change, so we
|
|
3242
3361
|
// need to manually change it ourselves
|
|
3243
|
-
if (isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ |
|
|
3244
|
-
16 /* NAVIGATION_DUPLICATED */) &&
|
|
3362
|
+
if (isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ |
|
|
3363
|
+
16 /* ErrorTypes.NAVIGATION_DUPLICATED */) &&
|
|
3245
3364
|
!info.delta &&
|
|
3246
3365
|
info.type === NavigationType.pop) {
|
|
3247
3366
|
routerHistory.go(-1, false);
|
|
@@ -3252,8 +3371,9 @@ function createRouter(options) {
|
|
|
3252
3371
|
return Promise.reject();
|
|
3253
3372
|
}
|
|
3254
3373
|
// do not restore history on unknown direction
|
|
3255
|
-
if (info.delta)
|
|
3374
|
+
if (info.delta) {
|
|
3256
3375
|
routerHistory.go(-info.delta, false);
|
|
3376
|
+
}
|
|
3257
3377
|
// unrecognized error, transfer to the global handler
|
|
3258
3378
|
return triggerError(error, toLocation, from);
|
|
3259
3379
|
})
|
|
@@ -3265,11 +3385,14 @@ function createRouter(options) {
|
|
|
3265
3385
|
toLocation, from, false);
|
|
3266
3386
|
// revert the navigation
|
|
3267
3387
|
if (failure) {
|
|
3268
|
-
if (info.delta
|
|
3388
|
+
if (info.delta &&
|
|
3389
|
+
// a new navigation has been triggered, so we do not want to revert, that will change the current history
|
|
3390
|
+
// entry while a different route is displayed
|
|
3391
|
+
!isNavigationFailure(failure, 8 /* ErrorTypes.NAVIGATION_CANCELLED */)) {
|
|
3269
3392
|
routerHistory.go(-info.delta, false);
|
|
3270
3393
|
}
|
|
3271
3394
|
else if (info.type === NavigationType.pop &&
|
|
3272
|
-
isNavigationFailure(failure, 4 /* NAVIGATION_ABORTED */ | 16 /* NAVIGATION_DUPLICATED */)) {
|
|
3395
|
+
isNavigationFailure(failure, 4 /* ErrorTypes.NAVIGATION_ABORTED */ | 16 /* ErrorTypes.NAVIGATION_DUPLICATED */)) {
|
|
3273
3396
|
// manual change in hash history #916
|
|
3274
3397
|
// it's like a push but lacks the information of the direction
|
|
3275
3398
|
routerHistory.go(-1, false);
|
|
@@ -3345,6 +3468,7 @@ function createRouter(options) {
|
|
|
3345
3468
|
const installedApps = new Set();
|
|
3346
3469
|
const router = {
|
|
3347
3470
|
currentRoute,
|
|
3471
|
+
listening: true,
|
|
3348
3472
|
addRoute,
|
|
3349
3473
|
removeRoute,
|
|
3350
3474
|
hasRoute,
|
|
@@ -3408,6 +3532,7 @@ function createRouter(options) {
|
|
|
3408
3532
|
}
|
|
3409
3533
|
unmountApp();
|
|
3410
3534
|
};
|
|
3535
|
+
// TODO: this probably needs to be updated so it can be used by kdu-termui
|
|
3411
3536
|
if (isBrowser) {
|
|
3412
3537
|
addDevtools(app, router, matcher);
|
|
3413
3538
|
}
|
|
@@ -3457,4 +3582,4 @@ function useRoute() {
|
|
|
3457
3582
|
return inject(routeLocationKey);
|
|
3458
3583
|
}
|
|
3459
3584
|
|
|
3460
|
-
export { NavigationFailureType, RouterLink, RouterView, START_LOCATION_NORMALIZED as START_LOCATION, createMemoryHistory, createRouter, createRouterMatcher, createWebHashHistory, createWebHistory, isNavigationFailure, matchedRouteKey, onBeforeRouteLeave, onBeforeRouteUpdate, parseQuery, routeLocationKey, routerKey, routerViewLocationKey, stringifyQuery, useLink, useRoute, useRouter, viewDepthKey };
|
|
3585
|
+
export { NavigationFailureType, RouterLink, RouterView, START_LOCATION_NORMALIZED as START_LOCATION, createMemoryHistory, createRouter, createRouterMatcher, createWebHashHistory, createWebHistory, isNavigationFailure, loadRouteLocation, matchedRouteKey, onBeforeRouteLeave, onBeforeRouteUpdate, parseQuery, routeLocationKey, routerKey, routerViewLocationKey, stringifyQuery, useLink, useRoute, useRouter, viewDepthKey };
|