kdu-router 3.1.7 → 3.5.4

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.
@@ -0,0 +1,86 @@
1
+ // When changing thing, also edit router.d.ts
2
+ export const NavigationFailureType = {
3
+ redirected: 2,
4
+ aborted: 4,
5
+ cancelled: 8,
6
+ duplicated: 16
7
+ }
8
+
9
+ export function createNavigationRedirectedError (from, to) {
10
+ return createRouterError(
11
+ from,
12
+ to,
13
+ NavigationFailureType.redirected,
14
+ `Redirected when going from "${from.fullPath}" to "${stringifyRoute(
15
+ to
16
+ )}" via a navigation guard.`
17
+ )
18
+ }
19
+
20
+ export function createNavigationDuplicatedError (from, to) {
21
+ const error = createRouterError(
22
+ from,
23
+ to,
24
+ NavigationFailureType.duplicated,
25
+ `Avoided redundant navigation to current location: "${from.fullPath}".`
26
+ )
27
+ // backwards compatible with the first introduction of Errors
28
+ error.name = 'NavigationDuplicated'
29
+ return error
30
+ }
31
+
32
+ export function createNavigationCancelledError (from, to) {
33
+ return createRouterError(
34
+ from,
35
+ to,
36
+ NavigationFailureType.cancelled,
37
+ `Navigation cancelled from "${from.fullPath}" to "${
38
+ to.fullPath
39
+ }" with a new navigation.`
40
+ )
41
+ }
42
+
43
+ export function createNavigationAbortedError (from, to) {
44
+ return createRouterError(
45
+ from,
46
+ to,
47
+ NavigationFailureType.aborted,
48
+ `Navigation aborted from "${from.fullPath}" to "${
49
+ to.fullPath
50
+ }" via a navigation guard.`
51
+ )
52
+ }
53
+
54
+ function createRouterError (from, to, type, message) {
55
+ const error = new Error(message)
56
+ error._isRouter = true
57
+ error.from = from
58
+ error.to = to
59
+ error.type = type
60
+
61
+ return error
62
+ }
63
+
64
+ const propertiesToLog = ['params', 'query', 'hash']
65
+
66
+ function stringifyRoute (to) {
67
+ if (typeof to === 'string') return to
68
+ if ('path' in to) return to.path
69
+ const location = {}
70
+ propertiesToLog.forEach(key => {
71
+ if (key in to) location[key] = to[key]
72
+ })
73
+ return JSON.stringify(location, null, 2)
74
+ }
75
+
76
+ export function isError (err) {
77
+ return Object.prototype.toString.call(err).indexOf('Error') > -1
78
+ }
79
+
80
+ export function isNavigationFailure (err, errorType) {
81
+ return (
82
+ isError(err) &&
83
+ err._isRouter &&
84
+ (errorType == null || err.type === errorType)
85
+ )
86
+ }
package/src/util/path.js CHANGED
@@ -70,5 +70,5 @@ export function parsePath (path: string): {
70
70
  }
71
71
 
72
72
  export function cleanPath (path: string): string {
73
- return path.replace(/\/\//g, '/')
73
+ return path.replace(/\/(?:\s*\/)+/g, '/')
74
74
  }
@@ -19,7 +19,7 @@ export const supportsPushState =
19
19
  return false
20
20
  }
21
21
 
22
- return window.history && 'pushState' in window.history
22
+ return window.history && typeof window.history.pushState === 'function'
23
23
  })()
24
24
 
25
25
  export function pushState (url?: string, replace?: boolean) {
package/src/util/query.js CHANGED
@@ -9,11 +9,21 @@ const commaRE = /%2C/g
9
9
  // fixed encodeURIComponent which is more conformant to RFC3986:
10
10
  // - escapes [!'()*]
11
11
  // - preserve commas
12
- const encode = str => encodeURIComponent(str)
13
- .replace(encodeReserveRE, encodeReserveReplacer)
14
- .replace(commaRE, ',')
12
+ const encode = str =>
13
+ encodeURIComponent(str)
14
+ .replace(encodeReserveRE, encodeReserveReplacer)
15
+ .replace(commaRE, ',')
15
16
 
16
- const decode = decodeURIComponent
17
+ export function decode (str: string) {
18
+ try {
19
+ return decodeURIComponent(str)
20
+ } catch (err) {
21
+ if (process.env.NODE_ENV !== 'production') {
22
+ warn(false, `Error decoding "${str}". Leaving it intact.`)
23
+ }
24
+ }
25
+ return str
26
+ }
17
27
 
18
28
  export function resolveQuery (
19
29
  query: ?string,
@@ -29,11 +39,16 @@ export function resolveQuery (
29
39
  parsedQuery = {}
30
40
  }
31
41
  for (const key in extraQuery) {
32
- parsedQuery[key] = extraQuery[key]
42
+ const value = extraQuery[key]
43
+ parsedQuery[key] = Array.isArray(value)
44
+ ? value.map(castQueryParamValue)
45
+ : castQueryParamValue(value)
33
46
  }
34
47
  return parsedQuery
35
48
  }
36
49
 
50
+ const castQueryParamValue = value => (value == null || typeof value === 'object' ? value : String(value))
51
+
37
52
  function parseQuery (query: string): Dictionary<string> {
38
53
  const res = {}
39
54
 
@@ -46,9 +61,7 @@ function parseQuery (query: string): Dictionary<string> {
46
61
  query.split('&').forEach(param => {
47
62
  const parts = param.replace(/\+/g, ' ').split('=')
48
63
  const key = decode(parts.shift())
49
- const val = parts.length > 0
50
- ? decode(parts.join('='))
51
- : null
64
+ const val = parts.length > 0 ? decode(parts.join('=')) : null
52
65
 
53
66
  if (res[key] === undefined) {
54
67
  res[key] = val
@@ -63,33 +76,38 @@ function parseQuery (query: string): Dictionary<string> {
63
76
  }
64
77
 
65
78
  export function stringifyQuery (obj: Dictionary<string>): string {
66
- const res = obj ? Object.keys(obj).map(key => {
67
- const val = obj[key]
68
-
69
- if (val === undefined) {
70
- return ''
71
- }
79
+ const res = obj
80
+ ? Object.keys(obj)
81
+ .map(key => {
82
+ const val = obj[key]
72
83
 
73
- if (val === null) {
74
- return encode(key)
75
- }
84
+ if (val === undefined) {
85
+ return ''
86
+ }
76
87
 
77
- if (Array.isArray(val)) {
78
- const result = []
79
- val.forEach(val2 => {
80
- if (val2 === undefined) {
81
- return
88
+ if (val === null) {
89
+ return encode(key)
82
90
  }
83
- if (val2 === null) {
84
- result.push(encode(key))
85
- } else {
86
- result.push(encode(key) + '=' + encode(val2))
91
+
92
+ if (Array.isArray(val)) {
93
+ const result = []
94
+ val.forEach(val2 => {
95
+ if (val2 === undefined) {
96
+ return
97
+ }
98
+ if (val2 === null) {
99
+ result.push(encode(key))
100
+ } else {
101
+ result.push(encode(key) + '=' + encode(val2))
102
+ }
103
+ })
104
+ return result.join('&')
87
105
  }
88
- })
89
- return result.join('&')
90
- }
91
106
 
92
- return encode(key) + '=' + encode(val)
93
- }).filter(x => x.length > 0).join('&') : null
107
+ return encode(key) + '=' + encode(val)
108
+ })
109
+ .filter(x => x.length > 0)
110
+ .join('&')
111
+ : null
94
112
  return res ? `?${res}` : ''
95
113
  }
@@ -1,7 +1,8 @@
1
1
  /* @flow */
2
2
 
3
3
  import { _Kdu } from '../install'
4
- import { warn, isError } from './warn'
4
+ import { warn } from './warn'
5
+ import { isError } from '../util/errors'
5
6
 
6
7
  export function resolveAsyncComponents (matched: Array<RouteRecord>): Function {
7
8
  return (to, from, next) => {
package/src/util/route.js CHANGED
@@ -70,23 +70,23 @@ function getFullPath (
70
70
  return (path || '/') + stringify(query) + hash
71
71
  }
72
72
 
73
- export function isSameRoute (a: Route, b: ?Route): boolean {
73
+ export function isSameRoute (a: Route, b: ?Route, onlyPath: ?boolean): boolean {
74
74
  if (b === START) {
75
75
  return a === b
76
76
  } else if (!b) {
77
77
  return false
78
78
  } else if (a.path && b.path) {
79
- return (
80
- a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') &&
79
+ return a.path.replace(trailingSlashRE, '') === b.path.replace(trailingSlashRE, '') && (onlyPath ||
81
80
  a.hash === b.hash &&
82
- isObjectEqual(a.query, b.query)
83
- )
81
+ isObjectEqual(a.query, b.query))
84
82
  } else if (a.name && b.name) {
85
83
  return (
86
84
  a.name === b.name &&
87
- a.hash === b.hash &&
85
+ (onlyPath || (
86
+ a.hash === b.hash &&
88
87
  isObjectEqual(a.query, b.query) &&
89
- isObjectEqual(a.params, b.params)
88
+ isObjectEqual(a.params, b.params))
89
+ )
90
90
  )
91
91
  } else {
92
92
  return false
@@ -96,14 +96,18 @@ export function isSameRoute (a: Route, b: ?Route): boolean {
96
96
  function isObjectEqual (a = {}, b = {}): boolean {
97
97
  // handle null value #1566
98
98
  if (!a || !b) return a === b
99
- const aKeys = Object.keys(a)
100
- const bKeys = Object.keys(b)
99
+ const aKeys = Object.keys(a).sort()
100
+ const bKeys = Object.keys(b).sort()
101
101
  if (aKeys.length !== bKeys.length) {
102
102
  return false
103
103
  }
104
- return aKeys.every(key => {
104
+ return aKeys.every((key, i) => {
105
105
  const aVal = a[key]
106
+ const bKey = bKeys[i]
107
+ if (bKey !== key) return false
106
108
  const bVal = b[key]
109
+ // query values can be null and undefined
110
+ if (aVal == null || bVal == null) return aVal === bVal
107
111
  // check nested equality
108
112
  if (typeof aVal === 'object' && typeof bVal === 'object') {
109
113
  return isObjectEqual(aVal, bVal)
@@ -130,3 +134,18 @@ function queryIncludes (current: Dictionary<string>, target: Dictionary<string>)
130
134
  }
131
135
  return true
132
136
  }
137
+
138
+ export function handleRouteEntered (route: Route) {
139
+ for (let i = 0; i < route.matched.length; i++) {
140
+ const record = route.matched[i]
141
+ for (const name in record.instances) {
142
+ const instance = record.instances[name]
143
+ const cbs = record.enteredCbs[name]
144
+ if (!instance || !cbs) continue
145
+ delete record.enteredCbs[name]
146
+ for (let i = 0; i < cbs.length; i++) {
147
+ if (!instance._isBeingDestroyed) cbs[i](instance)
148
+ }
149
+ }
150
+ }
151
+ }
@@ -8,6 +8,10 @@ import { extend } from './misc'
8
8
  const positionStore = Object.create(null)
9
9
 
10
10
  export function setupScroll () {
11
+ // Prevent browser scroll behavior on History popstate
12
+ if ('scrollRestoration' in window.history) {
13
+ window.history.scrollRestoration = 'manual'
14
+ }
11
15
  // Fix for #1585 for Firefox
12
16
  // Fix for #2195 Add optional third attribute to workaround a bug in safari https://bugs.webkit.org/show_bug.cgi?id=182678
13
17
  // Fix for #2774 Support for apps loaded from Windows file shares not mapped to network drives: replaced location.origin with
@@ -19,12 +23,10 @@ export function setupScroll () {
19
23
  const stateCopy = extend({}, window.history.state)
20
24
  stateCopy.key = getStateKey()
21
25
  window.history.replaceState(stateCopy, '', absolutePath)
22
- window.addEventListener('popstate', e => {
23
- saveScrollPosition()
24
- if (e.state && e.state.key) {
25
- setStateKey(e.state.key)
26
- }
27
- })
26
+ window.addEventListener('popstate', handlePopState)
27
+ return () => {
28
+ window.removeEventListener('popstate', handlePopState)
29
+ }
28
30
  }
29
31
 
30
32
  export function handleScroll (
@@ -86,6 +88,13 @@ export function saveScrollPosition () {
86
88
  }
87
89
  }
88
90
 
91
+ function handlePopState (e) {
92
+ saveScrollPosition()
93
+ if (e.state && e.state.key) {
94
+ setStateKey(e.state.key)
95
+ }
96
+ }
97
+
89
98
  function getScrollPosition (): ?Object {
90
99
  const key = getStateKey()
91
100
  if (key) {
@@ -151,6 +160,16 @@ function scrollToPosition (shouldScroll, position) {
151
160
  }
152
161
 
153
162
  if (position) {
154
- window.scrollTo(position.x, position.y)
163
+ // $flow-disable-line
164
+ if ('scrollBehavior' in document.documentElement.style) {
165
+ window.scrollTo({
166
+ left: position.x,
167
+ top: position.y,
168
+ // $flow-disable-line
169
+ behavior: shouldScroll.behavior
170
+ })
171
+ } else {
172
+ window.scrollTo(position.x, position.y)
173
+ }
155
174
  }
156
175
  }
package/src/util/warn.js CHANGED
@@ -7,19 +7,8 @@ export function assert (condition: any, message: string) {
7
7
  }
8
8
 
9
9
  export function warn (condition: any, message: string) {
10
- if (process.env.NODE_ENV !== 'production' && !condition) {
10
+ if (!condition) {
11
11
  typeof console !== 'undefined' && console.warn(`[kdu-router] ${message}`)
12
12
  }
13
13
  }
14
14
 
15
- export function isError (err: any): boolean {
16
- return Object.prototype.toString.call(err).indexOf('Error') > -1
17
- }
18
-
19
- export function isExtendedError (constructor: Function, err: any): boolean {
20
- return (
21
- err instanceof constructor ||
22
- // _name is to support IE9 too
23
- (err && (err.name === constructor.name || err._name === constructor._name))
24
- )
25
- }
package/types/index.d.ts CHANGED
@@ -5,12 +5,17 @@ export default KduRouter
5
5
 
6
6
  export {
7
7
  RouterMode,
8
+ RouteMeta,
8
9
  RawLocation,
9
10
  RedirectOption,
10
11
  RouterOptions,
11
12
  RouteConfig,
12
13
  RouteRecord,
14
+ RouteRecordPublic,
13
15
  Location,
14
16
  Route,
15
- NavigationGuard
17
+ NavigationGuard,
18
+ NavigationGuardNext,
19
+ NavigationFailureType,
20
+ NavigationFailure
16
21
  } from './router'
package/types/router.d.ts CHANGED
@@ -1,22 +1,27 @@
1
1
  import Kdu, { ComponentOptions, PluginFunction, AsyncComponent } from 'kdu'
2
2
 
3
3
  type Component = ComponentOptions<Kdu> | typeof Kdu | AsyncComponent
4
- type Dictionary < T > = { [key: string]: T }
4
+ type Dictionary<T> = { [key: string]: T }
5
5
  type ErrorHandler = (err: Error) => void
6
6
 
7
7
  export type RouterMode = 'hash' | 'history' | 'abstract'
8
8
  export type RawLocation = string | Location
9
9
  export type RedirectOption = RawLocation | ((to: Route) => RawLocation)
10
- export type NavigationGuard < V extends Kdu = Kdu > = (
10
+ export type NavigationGuardNext<V extends Kdu = Kdu> = (
11
+ to?: RawLocation | false | ((vm: V) => any) | void
12
+ ) => void
13
+
14
+ export type NavigationGuard<V extends Kdu = Kdu> = (
11
15
  to: Route,
12
16
  from: Route,
13
- next: (to?: RawLocation | false | ((vm: V) => any) | void) => void
17
+ next: NavigationGuardNext<V>
14
18
  ) => any
15
19
 
16
20
  export declare class KduRouter {
17
21
  constructor(options?: RouterOptions)
18
22
 
19
23
  app: Kdu
24
+ options: RouterOptions
20
25
  mode: RouterMode
21
26
  currentRoute: Route
22
27
 
@@ -38,10 +43,16 @@ export declare class KduRouter {
38
43
  go(n: number): void
39
44
  back(): void
40
45
  forward(): void
46
+ match (raw: RawLocation, current?: Route, redirectedFrom?: Location): Route
41
47
  getMatchedComponents(to?: RawLocation | Route): Component[]
42
48
  onReady(cb: Function, errorCb?: ErrorHandler): void
43
49
  onError(cb: ErrorHandler): void
44
50
  addRoutes(routes: RouteConfig[]): void
51
+
52
+ addRoute(parent: string, route: RouteConfig): void
53
+ addRoute(route: RouteConfig): void
54
+ getRoutes(): RouteRecordPublic[]
55
+
45
56
  resolve(
46
57
  to: RawLocation,
47
58
  current?: Route,
@@ -56,10 +67,34 @@ export declare class KduRouter {
56
67
  }
57
68
 
58
69
  static install: PluginFunction<never>
70
+ static version: string
71
+
72
+ static isNavigationFailure: (
73
+ error: any,
74
+ type?: number
75
+ ) => error is NavigationFailure
76
+ static NavigationFailureType: {
77
+ [k in keyof typeof NavigationFailureType]: NavigationFailureType
78
+ }
79
+
80
+ static START_LOCATION: Route
81
+ }
82
+
83
+ export enum NavigationFailureType {
84
+ redirected = 2,
85
+ aborted = 4,
86
+ cancelled = 8,
87
+ duplicated = 16
88
+ }
89
+
90
+ export interface NavigationFailure extends Error {
91
+ to: Route
92
+ from: Route
93
+ type: number
59
94
  }
60
95
 
61
96
  type Position = { x: number; y: number }
62
- type PositionResult = Position | { selector: string; offset?: Position } | void
97
+ type PositionResult = Position | { selector: string; offset?: Position, behavior?: ScrollBehavior } | void
63
98
 
64
99
  export interface RouterOptions {
65
100
  routes?: RouteConfig[]
@@ -85,21 +120,30 @@ export interface PathToRegexpOptions {
85
120
  end?: boolean
86
121
  }
87
122
 
88
- export interface RouteConfig {
123
+ interface _RouteConfigBase {
89
124
  path: string
90
125
  name?: string
91
- component?: Component
92
- components?: Dictionary<Component>
126
+ children?: RouteConfig[]
93
127
  redirect?: RedirectOption
94
128
  alias?: string | string[]
95
- children?: RouteConfig[]
96
- meta?: any
129
+ meta?: RouteMeta
97
130
  beforeEnter?: NavigationGuard
98
- props?: boolean | Object | RoutePropsFunction
99
131
  caseSensitive?: boolean
100
132
  pathToRegexpOptions?: PathToRegexpOptions
101
133
  }
102
134
 
135
+ interface RouteConfigSingleView extends _RouteConfigBase {
136
+ component?: Component
137
+ props?: boolean | Object | RoutePropsFunction
138
+ }
139
+
140
+ interface RouteConfigMultipleViews extends _RouteConfigBase {
141
+ components?: Dictionary<Component>
142
+ props?: Dictionary<boolean | Object | RoutePropsFunction>
143
+ }
144
+
145
+ export type RouteConfig = RouteConfigSingleView | RouteConfigMultipleViews
146
+
103
147
  export interface RouteRecord {
104
148
  path: string
105
149
  regex: RegExp
@@ -109,6 +153,25 @@ export interface RouteRecord {
109
153
  parent?: RouteRecord
110
154
  redirect?: RedirectOption
111
155
  matchAs?: string
156
+ meta: RouteMeta
157
+ beforeEnter?: (
158
+ route: Route,
159
+ redirect: (location: RawLocation) => void,
160
+ next: () => void
161
+ ) => any
162
+ props:
163
+ | boolean
164
+ | Object
165
+ | RoutePropsFunction
166
+ | Dictionary<boolean | Object | RoutePropsFunction>
167
+ }
168
+
169
+ export interface RouteRecordPublic {
170
+ path: string
171
+ components: Dictionary<Component>
172
+ instances: Dictionary<Kdu>
173
+ name?: string
174
+ redirect?: RedirectOption
112
175
  meta: any
113
176
  beforeEnter?: (
114
177
  route: Route,
@@ -122,6 +185,7 @@ export interface RouteRecord {
122
185
  | Dictionary<boolean | Object | RoutePropsFunction>
123
186
  }
124
187
 
188
+
125
189
  export interface Location {
126
190
  name?: string
127
191
  path?: string
@@ -141,5 +205,7 @@ export interface Route {
141
205
  fullPath: string
142
206
  matched: RouteRecord[]
143
207
  redirectedFrom?: string
144
- meta?: any
208
+ meta?: RouteMeta
145
209
  }
210
+
211
+ export interface RouteMeta extends Record<string | number | symbol, any> {}
@@ -1,22 +0,0 @@
1
- export class NavigationDuplicated extends Error {
2
- constructor (normalizedLocation) {
3
- super()
4
- this.name = this._name = 'NavigationDuplicated'
5
- // passing the message to super() doesn't seem to work in the transpiled version
6
- this.message = `Navigating to current location ("${
7
- normalizedLocation.fullPath
8
- }") is not allowed`
9
- // add a stack property so services like Sentry can correctly display it
10
- Object.defineProperty(this, 'stack', {
11
- value: new Error().stack,
12
- writable: true,
13
- configurable: true
14
- })
15
- // we could also have used
16
- // Error.captureStackTrace(this, this.constructor)
17
- // but it only exists on node and chrome
18
- }
19
- }
20
-
21
- // support IE9
22
- NavigationDuplicated._name = 'NavigationDuplicated'