kdu-router 3.4.0-beta.0 → 3.6.0

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.
Files changed (49) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +11 -9
  3. package/dist/composables.js +253 -0
  4. package/dist/composables.mjs +244 -0
  5. package/dist/kdu-router.common.js +2682 -2572
  6. package/dist/kdu-router.esm.browser.js +2677 -2563
  7. package/dist/kdu-router.esm.browser.min.js +5 -5
  8. package/dist/kdu-router.esm.js +2685 -2572
  9. package/dist/kdu-router.js +2682 -2573
  10. package/dist/kdu-router.min.js +5 -5
  11. package/ketur/attributes.json +38 -38
  12. package/ketur/tags.json +20 -20
  13. package/package.json +115 -111
  14. package/src/components/link.js +224 -197
  15. package/src/components/view.js +155 -149
  16. package/src/composables/globals.js +34 -0
  17. package/src/composables/guards.js +68 -0
  18. package/src/composables/index.js +3 -0
  19. package/src/composables/useLink.js +113 -0
  20. package/src/composables/utils.js +11 -0
  21. package/src/create-matcher.js +226 -200
  22. package/src/create-route-map.js +220 -205
  23. package/src/entries/cjs.js +3 -0
  24. package/src/entries/esm.js +12 -0
  25. package/src/history/abstract.js +72 -68
  26. package/src/history/base.js +379 -400
  27. package/src/history/hash.js +152 -163
  28. package/src/history/html5.js +99 -94
  29. package/src/index.js +3 -277
  30. package/src/install.js +52 -52
  31. package/src/router.js +293 -0
  32. package/src/util/async.js +18 -18
  33. package/src/util/dom.js +3 -3
  34. package/src/util/errors.js +86 -85
  35. package/src/util/location.js +69 -69
  36. package/src/util/misc.js +6 -6
  37. package/src/util/params.js +37 -37
  38. package/src/util/path.js +74 -74
  39. package/src/util/push-state.js +46 -46
  40. package/src/util/query.js +113 -96
  41. package/src/util/resolve-components.js +109 -109
  42. package/src/util/route.js +151 -132
  43. package/src/util/scroll.js +175 -165
  44. package/src/util/state-key.js +22 -22
  45. package/src/util/warn.js +14 -14
  46. package/types/composables.d.ts +53 -0
  47. package/types/index.d.ts +25 -17
  48. package/types/kdu.d.ts +22 -22
  49. package/types/router.d.ts +564 -170
@@ -1,149 +1,155 @@
1
- import { warn } from '../util/warn'
2
- import { extend } from '../util/misc'
3
-
4
- export default {
5
- name: 'RouterView',
6
- functional: true,
7
- props: {
8
- name: {
9
- type: String,
10
- default: 'default'
11
- }
12
- },
13
- render (_, { props, children, parent, data }) {
14
- // used by devtools to display a router-view badge
15
- data.routerView = true
16
-
17
- // directly use parent context's createElement() function
18
- // so that components rendered by router-view can resolve named slots
19
- const h = parent.$createElement
20
- const name = props.name
21
- const route = parent.$route
22
- const cache = parent._routerViewCache || (parent._routerViewCache = {})
23
-
24
- // determine current view depth, also check to see if the tree
25
- // has been toggled inactive but kept-alive.
26
- let depth = 0
27
- let inactive = false
28
- while (parent && parent._routerRoot !== parent) {
29
- const knodeData = parent.$knode ? parent.$knode.data : {}
30
- if (knodeData.routerView) {
31
- depth++
32
- }
33
- if (knodeData.keepAlive && parent._directInactive && parent._inactive) {
34
- inactive = true
35
- }
36
- parent = parent.$parent
37
- }
38
- data.routerViewDepth = depth
39
-
40
- // render previous view if the tree is inactive and kept-alive
41
- if (inactive) {
42
- const cachedData = cache[name]
43
- const cachedComponent = cachedData && cachedData.component
44
- if (cachedComponent) {
45
- // #2301
46
- // pass props
47
- if (cachedData.configProps) {
48
- fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps)
49
- }
50
- return h(cachedComponent, data, children)
51
- } else {
52
- // render previous empty view
53
- return h()
54
- }
55
- }
56
-
57
- const matched = route.matched[depth]
58
- const component = matched && matched.components[name]
59
-
60
- // render empty node if no matched route or no config component
61
- if (!matched || !component) {
62
- cache[name] = null
63
- return h()
64
- }
65
-
66
- // cache component
67
- cache[name] = { component }
68
-
69
- // attach instance registration hook
70
- // this will be called in the instance's injected lifecycle hooks
71
- data.registerRouteInstance = (vm, val) => {
72
- // val could be undefined for unregistration
73
- const current = matched.instances[name]
74
- if (
75
- (val && current !== vm) ||
76
- (!val && current === vm)
77
- ) {
78
- matched.instances[name] = val
79
- }
80
- }
81
-
82
- // also register instance in prepatch hook
83
- // in case the same component instance is reused across different routes
84
- ;(data.hook || (data.hook = {})).prepatch = (_, knode) => {
85
- matched.instances[name] = knode.componentInstance
86
- }
87
-
88
- // register instance in init hook
89
- // in case kept-alive component be actived when routes changed
90
- data.hook.init = (knode) => {
91
- if (knode.data.keepAlive &&
92
- knode.componentInstance &&
93
- knode.componentInstance !== matched.instances[name]
94
- ) {
95
- matched.instances[name] = knode.componentInstance
96
- }
97
- }
98
-
99
- const configProps = matched.props && matched.props[name]
100
- // save route and configProps in cache
101
- if (configProps) {
102
- extend(cache[name], {
103
- route,
104
- configProps
105
- })
106
- fillPropsinData(component, data, route, configProps)
107
- }
108
-
109
- return h(component, data, children)
110
- }
111
- }
112
-
113
- function fillPropsinData (component, data, route, configProps) {
114
- // resolve props
115
- let propsToPass = data.props = resolveProps(route, configProps)
116
- if (propsToPass) {
117
- // clone to prevent mutation
118
- propsToPass = data.props = extend({}, propsToPass)
119
- // pass non-declared props as attrs
120
- const attrs = data.attrs = data.attrs || {}
121
- for (const key in propsToPass) {
122
- if (!component.props || !(key in component.props)) {
123
- attrs[key] = propsToPass[key]
124
- delete propsToPass[key]
125
- }
126
- }
127
- }
128
- }
129
-
130
- function resolveProps (route, config) {
131
- switch (typeof config) {
132
- case 'undefined':
133
- return
134
- case 'object':
135
- return config
136
- case 'function':
137
- return config(route)
138
- case 'boolean':
139
- return config ? route.params : undefined
140
- default:
141
- if (process.env.NODE_ENV !== 'production') {
142
- warn(
143
- false,
144
- `props in "${route.path}" is a ${typeof config}, ` +
145
- `expecting an object, function or boolean.`
146
- )
147
- }
148
- }
149
- }
1
+ import { warn } from '../util/warn'
2
+ import { extend } from '../util/misc'
3
+ import { handleRouteEntered } from '../util/route'
4
+
5
+ export default {
6
+ name: 'RouterView',
7
+ functional: true,
8
+ props: {
9
+ name: {
10
+ type: String,
11
+ default: 'default'
12
+ }
13
+ },
14
+ render (_, { props, children, parent, data }) {
15
+ // used by devtools to display a router-view badge
16
+ data.routerView = true
17
+
18
+ // directly use parent context's createElement() function
19
+ // so that components rendered by router-view can resolve named slots
20
+ const h = parent.$createElement
21
+ const name = props.name
22
+ const route = parent.$route
23
+ const cache = parent._routerViewCache || (parent._routerViewCache = {})
24
+
25
+ // determine current view depth, also check to see if the tree
26
+ // has been toggled inactive but kept-alive.
27
+ let depth = 0
28
+ let inactive = false
29
+ while (parent && parent._routerRoot !== parent) {
30
+ const knodeData = parent.$knode ? parent.$knode.data : {}
31
+ if (knodeData.routerView) {
32
+ depth++
33
+ }
34
+ if (knodeData.keepAlive && parent._directInactive && parent._inactive) {
35
+ inactive = true
36
+ }
37
+ parent = parent.$parent
38
+ }
39
+ data.routerViewDepth = depth
40
+
41
+ // render previous view if the tree is inactive and kept-alive
42
+ if (inactive) {
43
+ const cachedData = cache[name]
44
+ const cachedComponent = cachedData && cachedData.component
45
+ if (cachedComponent) {
46
+ // #2301
47
+ // pass props
48
+ if (cachedData.configProps) {
49
+ fillPropsinData(cachedComponent, data, cachedData.route, cachedData.configProps)
50
+ }
51
+ return h(cachedComponent, data, children)
52
+ } else {
53
+ // render previous empty view
54
+ return h()
55
+ }
56
+ }
57
+
58
+ const matched = route.matched[depth]
59
+ const component = matched && matched.components[name]
60
+
61
+ // render empty node if no matched route or no config component
62
+ if (!matched || !component) {
63
+ cache[name] = null
64
+ return h()
65
+ }
66
+
67
+ // cache component
68
+ cache[name] = { component }
69
+
70
+ // attach instance registration hook
71
+ // this will be called in the instance's injected lifecycle hooks
72
+ data.registerRouteInstance = (vm, val) => {
73
+ // val could be undefined for unregistration
74
+ const current = matched.instances[name]
75
+ if (
76
+ (val && current !== vm) ||
77
+ (!val && current === vm)
78
+ ) {
79
+ matched.instances[name] = val
80
+ }
81
+ }
82
+
83
+ // also register instance in prepatch hook
84
+ // in case the same component instance is reused across different routes
85
+ ;(data.hook || (data.hook = {})).prepatch = (_, knode) => {
86
+ matched.instances[name] = knode.componentInstance
87
+ }
88
+
89
+ // register instance in init hook
90
+ // in case kept-alive component be actived when routes changed
91
+ data.hook.init = (knode) => {
92
+ if (knode.data.keepAlive &&
93
+ knode.componentInstance &&
94
+ knode.componentInstance !== matched.instances[name]
95
+ ) {
96
+ matched.instances[name] = knode.componentInstance
97
+ }
98
+
99
+ // if the route transition has already been confirmed then we weren't
100
+ // able to call the cbs during confirmation as the component was not
101
+ // registered yet, so we call it here.
102
+ handleRouteEntered(route)
103
+ }
104
+
105
+ const configProps = matched.props && matched.props[name]
106
+ // save route and configProps in cache
107
+ if (configProps) {
108
+ extend(cache[name], {
109
+ route,
110
+ configProps
111
+ })
112
+ fillPropsinData(component, data, route, configProps)
113
+ }
114
+
115
+ return h(component, data, children)
116
+ }
117
+ }
118
+
119
+ function fillPropsinData (component, data, route, configProps) {
120
+ // resolve props
121
+ let propsToPass = data.props = resolveProps(route, configProps)
122
+ if (propsToPass) {
123
+ // clone to prevent mutation
124
+ propsToPass = data.props = extend({}, propsToPass)
125
+ // pass non-declared props as attrs
126
+ const attrs = data.attrs = data.attrs || {}
127
+ for (const key in propsToPass) {
128
+ if (!component.props || !(key in component.props)) {
129
+ attrs[key] = propsToPass[key]
130
+ delete propsToPass[key]
131
+ }
132
+ }
133
+ }
134
+ }
135
+
136
+ function resolveProps (route, config) {
137
+ switch (typeof config) {
138
+ case 'undefined':
139
+ return
140
+ case 'object':
141
+ return config
142
+ case 'function':
143
+ return config(route)
144
+ case 'boolean':
145
+ return config ? route.params : undefined
146
+ default:
147
+ if (process.env.NODE_ENV !== 'production') {
148
+ warn(
149
+ false,
150
+ `props in "${route.path}" is a ${typeof config}, ` +
151
+ `expecting an object, function or boolean.`
152
+ )
153
+ }
154
+ }
155
+ }
@@ -0,0 +1,34 @@
1
+ import {
2
+ getCurrentInstance,
3
+ shallowReactive,
4
+ effectScope
5
+ } from 'kdu'
6
+ import { throwNoCurrentInstance } from './utils'
7
+
8
+ export function useRouter () {
9
+ if (process.env.NODE_ENV !== 'production') {
10
+ throwNoCurrentInstance('useRouter')
11
+ }
12
+
13
+ return getCurrentInstance().proxy.$root.$router
14
+ }
15
+
16
+ export function useRoute () {
17
+ if (process.env.NODE_ENV !== 'production') {
18
+ throwNoCurrentInstance('useRoute')
19
+ }
20
+
21
+ const root = getCurrentInstance().proxy.$root
22
+ if (!root._$route) {
23
+ const route = effectScope(true).run(() =>
24
+ shallowReactive(Object.assign({}, root.$router.currentRoute))
25
+ )
26
+ root._$route = route
27
+
28
+ root.$router.afterEach(to => {
29
+ Object.assign(route, to)
30
+ })
31
+ }
32
+
33
+ return root._$route
34
+ }
@@ -0,0 +1,68 @@
1
+ import { getCurrentInstance, onUnmounted } from 'kdu'
2
+ import { throwNoCurrentInstance } from './utils'
3
+ import { useRouter } from './globals'
4
+
5
+ export function onBeforeRouteUpdate (guard) {
6
+ if (process.env.NODE_ENV !== 'production') {
7
+ throwNoCurrentInstance('onBeforeRouteUpdate')
8
+ }
9
+
10
+ return useFilteredGuard(guard, isUpdateNavigation)
11
+ }
12
+ function isUpdateNavigation (to, from, depth) {
13
+ const toMatched = to.matched
14
+ const fromMatched = from.matched
15
+ return (
16
+ toMatched.length >= depth &&
17
+ toMatched
18
+ .slice(0, depth + 1)
19
+ .every((record, i) => record === fromMatched[i])
20
+ )
21
+ }
22
+
23
+ function isLeaveNavigation (to, from, depth) {
24
+ const toMatched = to.matched
25
+ const fromMatched = from.matched
26
+ return toMatched.length < depth || toMatched[depth] !== fromMatched[depth]
27
+ }
28
+
29
+ export function onBeforeRouteLeave (guard) {
30
+ if (process.env.NODE_ENV !== 'production') {
31
+ throwNoCurrentInstance('onBeforeRouteLeave')
32
+ }
33
+
34
+ return useFilteredGuard(guard, isLeaveNavigation)
35
+ }
36
+
37
+ const noop = () => {}
38
+ function useFilteredGuard (guard, fn) {
39
+ const instance = getCurrentInstance()
40
+ const router = useRouter()
41
+
42
+ let target = instance.proxy
43
+ // find the nearest RouterView to know the depth
44
+ while (
45
+ target &&
46
+ target.$knode &&
47
+ target.$knode.data &&
48
+ target.$knode.data.routerViewDepth == null
49
+ ) {
50
+ target = target.$parent
51
+ }
52
+
53
+ const depth =
54
+ target && target.$knode && target.$knode.data
55
+ ? target.$knode.data.routerViewDepth
56
+ : null
57
+
58
+ if (depth != null) {
59
+ const removeGuard = router.beforeEach((to, from, next) => {
60
+ return fn(to, from, depth) ? guard(to, from, next) : next()
61
+ })
62
+
63
+ onUnmounted(removeGuard)
64
+ return removeGuard
65
+ }
66
+
67
+ return noop
68
+ }
@@ -0,0 +1,3 @@
1
+ export * from './guards'
2
+ export * from './globals'
3
+ export * from './useLink'
@@ -0,0 +1,113 @@
1
+ import { computed, unref } from 'kdu'
2
+ import { guardEvent } from '../components/link'
3
+ import { throwNoCurrentInstance } from './utils'
4
+ import { useRouter, useRoute } from './globals'
5
+
6
+ function includesParams (outer, inner) {
7
+ for (const key in inner) {
8
+ const innerValue = inner[key]
9
+ const outerValue = outer[key]
10
+ if (typeof innerValue === 'string') {
11
+ if (innerValue !== outerValue) return false
12
+ } else {
13
+ if (
14
+ !Array.isArray(outerValue) ||
15
+ outerValue.length !== innerValue.length ||
16
+ innerValue.some((value, i) => value !== outerValue[i])
17
+ ) {
18
+ return false
19
+ }
20
+ }
21
+ }
22
+
23
+ return true
24
+ }
25
+
26
+ // helpers from kdu router 4
27
+
28
+ function isSameRouteLocationParamsValue (a, b) {
29
+ return Array.isArray(a)
30
+ ? isEquivalentArray(a, b)
31
+ : Array.isArray(b)
32
+ ? isEquivalentArray(b, a)
33
+ : a === b
34
+ }
35
+
36
+ function isEquivalentArray (a, b) {
37
+ return Array.isArray(b)
38
+ ? a.length === b.length && a.every((value, i) => value === b[i])
39
+ : a.length === 1 && a[0] === b
40
+ }
41
+
42
+ export function isSameRouteLocationParams (a, b) {
43
+ if (Object.keys(a).length !== Object.keys(b).length) return false
44
+
45
+ for (const key in a) {
46
+ if (!isSameRouteLocationParamsValue(a[key], b[key])) return false
47
+ }
48
+
49
+ return true
50
+ }
51
+
52
+ export function useLink (props) {
53
+ if (process.env.NODE_ENV !== 'production') {
54
+ throwNoCurrentInstance('useLink')
55
+ }
56
+
57
+ const router = useRouter()
58
+ const currentRoute = useRoute()
59
+
60
+ const resolvedRoute = computed(() => router.resolve(unref(props.to), currentRoute))
61
+
62
+ const activeRecordIndex = computed(() => {
63
+ const route = resolvedRoute.value.route
64
+ const { matched } = route
65
+ const { length } = matched
66
+ const routeMatched = matched[length - 1]
67
+ const currentMatched = currentRoute.matched
68
+ if (!routeMatched || !currentMatched.length) return -1
69
+ const index = currentMatched.indexOf(routeMatched)
70
+ if (index > -1) return index
71
+ // possible parent record
72
+ const parentRecord = currentMatched[currentMatched.length - 2]
73
+
74
+ return (
75
+ // we are dealing with nested routes
76
+ length > 1 &&
77
+ // if the parent and matched route have the same path, this link is
78
+ // referring to the empty child. Or we currently are on a different
79
+ // child of the same parent
80
+ parentRecord && parentRecord === routeMatched.parent
81
+ )
82
+ })
83
+
84
+ const isActive = computed(
85
+ () =>
86
+ activeRecordIndex.value > -1 &&
87
+ includesParams(currentRoute.params, resolvedRoute.value.route.params)
88
+ )
89
+ const isExactActive = computed(
90
+ () =>
91
+ activeRecordIndex.value > -1 &&
92
+ activeRecordIndex.value === currentRoute.matched.length - 1 &&
93
+ isSameRouteLocationParams(currentRoute.params, resolvedRoute.value.route.params)
94
+ )
95
+
96
+ const navigate = e => {
97
+ const href = resolvedRoute.value.route
98
+ if (guardEvent(e)) {
99
+ return props.replace
100
+ ? router.replace(href)
101
+ : router.push(href)
102
+ }
103
+ return Promise.resolve()
104
+ }
105
+
106
+ return {
107
+ href: computed(() => resolvedRoute.value.href),
108
+ route: computed(() => resolvedRoute.value.route),
109
+ isExactActive,
110
+ isActive,
111
+ navigate
112
+ }
113
+ }
@@ -0,0 +1,11 @@
1
+ import { getCurrentInstance } from 'kdu'
2
+
3
+ // dev only warn if no current instance
4
+
5
+ export function throwNoCurrentInstance (method) {
6
+ if (!getCurrentInstance()) {
7
+ throw new Error(
8
+ `[kdu-router]: Missing current instance. ${method}() must be called inside <script setup> or setup().`
9
+ )
10
+ }
11
+ }