space-router 0.9.5 → 1.0.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.
- package/README.md +1 -0
- package/dist/history.d.ts +12 -0
- package/dist/history.js +98 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +3 -0
- package/dist/match.d.ts +16 -0
- package/dist/match.js +68 -0
- package/dist/qs.d.ts +5 -0
- package/dist/qs.js +27 -0
- package/dist/router.d.ts +42 -0
- package/dist/router.js +131 -0
- package/package.json +29 -22
- package/src/history.ts +116 -0
- package/src/index.ts +7 -0
- package/src/match.ts +88 -0
- package/src/qs.ts +33 -0
- package/src/router.ts +197 -0
- package/.prettierignore +0 -5
- package/.prettierrc +0 -6
- package/.swc-cjs +0 -11
- package/.swc-esm +0 -1
- package/CHANGELOG.md +0 -68
- package/dist/cjs/history.js +0 -100
- package/dist/cjs/index.js +0 -27
- package/dist/cjs/match.js +0 -94
- package/dist/cjs/qs.js +0 -27
- package/dist/cjs/router.js +0 -279
- package/dist/esm/history.js +0 -90
- package/dist/esm/index.js +0 -3
- package/dist/esm/match.js +0 -76
- package/dist/esm/qs.js +0 -17
- package/dist/esm/router.js +0 -258
- package/docs/archetypes/default.md +0 -7
- package/docs/assets/js/bg.js +0 -742
- package/docs/assets/styles/base.scss +0 -2816
- package/docs/assets/styles/main.scss +0 -1368
- package/docs/assets/styles/syntax-m.scss +0 -264
- package/docs/assets/styles/syntax.scss +0 -240
- package/docs/config.toml +0 -9
- package/docs/content/_index.md +0 -189
- package/docs/layouts/404.html +0 -0
- package/docs/layouts/_default/baseof.html +0 -68
- package/docs/layouts/index.html +0 -31
- package/docs/layouts/partials/favicon.html +0 -5
- package/docs/layouts/partials/header.html +0 -0
- package/docs/layouts/partials/meta/name-author.html +0 -6
- package/docs/layouts/partials/meta/ogimage.html +0 -8
- package/docs/layouts/partials/site-verification.html +0 -12
- package/docs/layouts/shortcodes/callout.html +0 -1
- package/docs/static/js/highlightjs-9.15.10.min.js +0 -2
- package/docs/static/js/master.js +0 -0
- package/docs/static/js/tocbot.min.js +0 -18
- package/docs/static/space.png +0 -0
- package/oxlintrc.json +0 -6
- package/src/history.js +0 -97
- package/src/index.js +0 -3
- package/src/match.js +0 -87
- package/src/qs.js +0 -20
- package/src/router.js +0 -147
- package/tasks/build.js +0 -16
- package/test/flatten.test.js +0 -58
- package/test/match.test.js +0 -68
- package/test/router.test.js +0 -157
package/src/match.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { Qs } from './qs.ts'
|
|
2
|
+
|
|
3
|
+
export interface MatchedRoute {
|
|
4
|
+
pattern: string
|
|
5
|
+
url: string
|
|
6
|
+
pathname: string
|
|
7
|
+
params: Record<string, string>
|
|
8
|
+
query: Record<string, string>
|
|
9
|
+
search: string
|
|
10
|
+
hash: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface RouteEntry {
|
|
14
|
+
pattern: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function match(routes: RouteEntry[], url: string | undefined, qs: Qs): MatchedRoute | undefined {
|
|
18
|
+
if (!url) return
|
|
19
|
+
for (let i = 0; i < routes.length; i++) {
|
|
20
|
+
const m = matchOne(routes[i].pattern, url, qs)
|
|
21
|
+
if (m) return m
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function matchOne(pattern: string, url: string, qs?: Qs): MatchedRoute | undefined {
|
|
26
|
+
if (!pattern) return
|
|
27
|
+
|
|
28
|
+
const re = /(?:\?([^#]*))?(#.*)?$/
|
|
29
|
+
const originalUrl = url
|
|
30
|
+
const originalPattern = pattern
|
|
31
|
+
const c = url.match(re)
|
|
32
|
+
const params: Record<string, string> = {}
|
|
33
|
+
let query: Record<string, string> = {}
|
|
34
|
+
let search = ''
|
|
35
|
+
let hash = ''
|
|
36
|
+
|
|
37
|
+
if (c && c[1]) {
|
|
38
|
+
search = '?' + c[1]
|
|
39
|
+
query = qs ? qs.parse(c[1]) : {}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (c && c[2]) {
|
|
43
|
+
hash = c[2]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (pattern !== '*') {
|
|
47
|
+
const urlSegs = segmentize(url.replace(re, ''))
|
|
48
|
+
const patSegs = segmentize(pattern)
|
|
49
|
+
const max = Math.max(urlSegs.length, patSegs.length)
|
|
50
|
+
for (let i = 0; i < max; i++) {
|
|
51
|
+
const ps = patSegs[i]
|
|
52
|
+
if (ps && ps.charAt(0) === ':') {
|
|
53
|
+
const param = ps.replace(/(^:|[+*?]+$)/g, '')
|
|
54
|
+
const flags = ps.match(/[+*?]+$/)?.[0] ?? ''
|
|
55
|
+
const plus = flags.indexOf('+') > -1
|
|
56
|
+
const star = flags.indexOf('*') > -1
|
|
57
|
+
const val = urlSegs[i] || ''
|
|
58
|
+
if (!val && !star && (flags.indexOf('?') < 0 || plus)) return
|
|
59
|
+
|
|
60
|
+
params[param] = decodeURIComponent(val)
|
|
61
|
+
if (plus || star) {
|
|
62
|
+
params[param] = urlSegs.slice(i).map(decodeURIComponent).join('/')
|
|
63
|
+
break
|
|
64
|
+
}
|
|
65
|
+
} else if (ps !== urlSegs[i]) {
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
pattern: originalPattern,
|
|
73
|
+
url: originalUrl,
|
|
74
|
+
pathname: originalUrl.replace(re, ''),
|
|
75
|
+
params,
|
|
76
|
+
query,
|
|
77
|
+
search,
|
|
78
|
+
hash,
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function segmentize(url: string): string[] {
|
|
83
|
+
return strip(url).split('/')
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function strip(url: string): string {
|
|
87
|
+
return url.replace(/(^\/+|\/+$)/g, '')
|
|
88
|
+
}
|
package/src/qs.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export interface Qs {
|
|
2
|
+
parse(queryString: string): Record<string, string>
|
|
3
|
+
stringify(query: Record<string, unknown>): string
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const qs: Qs = {
|
|
7
|
+
parse(queryString) {
|
|
8
|
+
return queryString.split('&').reduce<Record<string, string>>((acc, pair) => {
|
|
9
|
+
if (!pair) return acc
|
|
10
|
+
const i = pair.indexOf('=')
|
|
11
|
+
const key = decode(i < 0 ? pair : pair.slice(0, i))
|
|
12
|
+
const val = i < 0 ? '' : decode(pair.slice(i + 1))
|
|
13
|
+
acc[key] = val
|
|
14
|
+
return acc
|
|
15
|
+
}, {})
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
stringify(query) {
|
|
19
|
+
return Object.keys(query)
|
|
20
|
+
.reduce<string[]>((acc, key) => {
|
|
21
|
+
const value = query[key]
|
|
22
|
+
if (value !== undefined) {
|
|
23
|
+
acc.push(encodeURIComponent(key) + '=' + encodeURIComponent(value as string | number | boolean))
|
|
24
|
+
}
|
|
25
|
+
return acc
|
|
26
|
+
}, [])
|
|
27
|
+
.join('&')
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function decode(s: string): string {
|
|
32
|
+
return decodeURIComponent(s.replace(/\+/g, ' '))
|
|
33
|
+
}
|
package/src/router.ts
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { match as findMatch, type MatchedRoute } from './match.ts'
|
|
2
|
+
import { createHistory, type Mode } from './history.ts'
|
|
3
|
+
import { qs as defaultQs, type Qs } from './qs.ts'
|
|
4
|
+
|
|
5
|
+
export interface RouterOptions {
|
|
6
|
+
mode?: Mode
|
|
7
|
+
qs?: Qs
|
|
8
|
+
sync?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Route<Data = Record<string, unknown>> extends MatchedRoute {
|
|
12
|
+
data: Data[]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface NavigateTarget {
|
|
16
|
+
url?: string
|
|
17
|
+
pathname?: string
|
|
18
|
+
params?: Record<string, string | number>
|
|
19
|
+
query?: Record<string, unknown> | null
|
|
20
|
+
hash?: string | null
|
|
21
|
+
replace?: boolean
|
|
22
|
+
merge?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type To = string | NavigateTarget
|
|
26
|
+
|
|
27
|
+
export type Redirect<Data = Record<string, unknown>> = To | ((route: Route<Data>) => To)
|
|
28
|
+
|
|
29
|
+
export type RouteDefinition<Data = Record<string, unknown>> = Data & {
|
|
30
|
+
path?: string
|
|
31
|
+
redirect?: Redirect<Data>
|
|
32
|
+
routes?: RouteDefinition<Data>[]
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface Router<Data = Record<string, unknown>> {
|
|
36
|
+
listen(routes: RouteDefinition<Data>[], onChange?: (route: Route<Data>) => void): () => void
|
|
37
|
+
navigate(to: To, curr?: Route<Data>): void
|
|
38
|
+
href(to: To, curr?: Route<Data>): string
|
|
39
|
+
match(url: string): Route<Data> | undefined
|
|
40
|
+
getUrl(): string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface FlatRoute {
|
|
44
|
+
pattern: string
|
|
45
|
+
data: Array<Record<string, unknown>>
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const PARAM_RE = /:([A-Za-z0-9_]+)([+*?])?/g
|
|
49
|
+
const MAX_REDIRECTS = 10
|
|
50
|
+
|
|
51
|
+
export function createRouter<Data = Record<string, unknown>>(options: RouterOptions = {}): Router<Data> {
|
|
52
|
+
const mode = options.mode || 'history'
|
|
53
|
+
const qs = options.qs || defaultQs
|
|
54
|
+
const sync = options.sync || false
|
|
55
|
+
const history = createHistory({ mode, sync })
|
|
56
|
+
|
|
57
|
+
let routes: FlatRoute[] = []
|
|
58
|
+
|
|
59
|
+
const router: Router<Data> = {
|
|
60
|
+
listen(routeMap, cb) {
|
|
61
|
+
routes = flatten(routeMap as RouteDefinition[])
|
|
62
|
+
let redirects = 0
|
|
63
|
+
return history.listen((url) => {
|
|
64
|
+
const route = router.match(url)
|
|
65
|
+
if (!route) {
|
|
66
|
+
redirects = 0
|
|
67
|
+
return
|
|
68
|
+
}
|
|
69
|
+
for (const r of route.data as Array<{ redirect?: Redirect<Data> }>) {
|
|
70
|
+
if (r.redirect) {
|
|
71
|
+
if (++redirects > MAX_REDIRECTS) {
|
|
72
|
+
redirects = 0
|
|
73
|
+
throw new Error('space-router: too many redirects')
|
|
74
|
+
}
|
|
75
|
+
const target = typeof r.redirect === 'function' ? r.redirect(route) : r.redirect
|
|
76
|
+
return router.navigate({ url: router.href(target), replace: true })
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
redirects = 0
|
|
80
|
+
if (cb) cb(route)
|
|
81
|
+
})
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
navigate(to, curr) {
|
|
85
|
+
const target: NavigateTarget = typeof to === 'string' ? { url: to } : to
|
|
86
|
+
const url = router.href(target, curr)
|
|
87
|
+
if (target.replace) {
|
|
88
|
+
history.replace(url)
|
|
89
|
+
} else {
|
|
90
|
+
history.push(url)
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
href(to, curr) {
|
|
95
|
+
// already a url
|
|
96
|
+
if (typeof to === 'string') {
|
|
97
|
+
return to
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// align with navigate API
|
|
101
|
+
if (to.url) {
|
|
102
|
+
return to.url
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
let target: NavigateTarget = to
|
|
106
|
+
if (target.merge) {
|
|
107
|
+
const c = curr || router.match(router.getUrl())
|
|
108
|
+
target = merge(c, target)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
let url = target.pathname || '/'
|
|
112
|
+
|
|
113
|
+
if (target.params) {
|
|
114
|
+
const params = target.params
|
|
115
|
+
url = url.replace(PARAM_RE, (m, name: string, flag: string | undefined) => {
|
|
116
|
+
const v = params[name]
|
|
117
|
+
if (v == null) return m
|
|
118
|
+
if (flag === '+' || flag === '*') {
|
|
119
|
+
return String(v).split('/').map(encodeURIComponent).join('/')
|
|
120
|
+
}
|
|
121
|
+
return encodeURIComponent(String(v))
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (target.query && Object.keys(target.query).length) {
|
|
126
|
+
const query = qs.stringify(target.query)
|
|
127
|
+
if (query) {
|
|
128
|
+
url = url + '?' + query
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (target.hash) {
|
|
133
|
+
const prefix = target.hash.startsWith('#') ? '' : '#'
|
|
134
|
+
url = url + prefix + target.hash
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return url
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
match(url) {
|
|
141
|
+
const route = findMatch(routes, url, qs)
|
|
142
|
+
if (route) {
|
|
143
|
+
return { ...route, data: data(routes, route) } as Route<Data>
|
|
144
|
+
}
|
|
145
|
+
return undefined
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
getUrl() {
|
|
149
|
+
return history.getUrl()
|
|
150
|
+
},
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return router
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function flatten(routeMap: RouteDefinition[]): FlatRoute[] {
|
|
157
|
+
const routes: FlatRoute[] = []
|
|
158
|
+
const parentData: Array<Record<string, unknown>> = []
|
|
159
|
+
function addLevel(level: RouteDefinition[]) {
|
|
160
|
+
level.forEach((route) => {
|
|
161
|
+
const {
|
|
162
|
+
path = '',
|
|
163
|
+
routes: children,
|
|
164
|
+
...routeData
|
|
165
|
+
} = route as RouteDefinition & {
|
|
166
|
+
path?: string
|
|
167
|
+
routes?: RouteDefinition[]
|
|
168
|
+
}
|
|
169
|
+
routes.push({ pattern: path, data: parentData.concat([routeData]) })
|
|
170
|
+
if (children) {
|
|
171
|
+
parentData.push(routeData)
|
|
172
|
+
addLevel(children)
|
|
173
|
+
parentData.pop()
|
|
174
|
+
}
|
|
175
|
+
})
|
|
176
|
+
}
|
|
177
|
+
addLevel(routeMap)
|
|
178
|
+
return routes
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function data(routes: FlatRoute[], matchingRoute: { pattern: string }) {
|
|
182
|
+
for (let i = 0; i < routes.length; i++) {
|
|
183
|
+
if (routes[i].pattern === matchingRoute.pattern) {
|
|
184
|
+
return routes[i].data
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return []
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function merge(curr: Partial<Route> | NavigateTarget | undefined, to: NavigateTarget): NavigateTarget {
|
|
191
|
+
const c = (curr || {}) as Partial<Route> & NavigateTarget
|
|
192
|
+
const pathname = to.pathname || c.pattern || c.pathname
|
|
193
|
+
const params = Object.assign({}, c.params, to.params)
|
|
194
|
+
const query = to.query === null ? null : Object.assign({}, c.query, to.query)
|
|
195
|
+
const hash = to.hash === null ? null : to.hash || c.hash || ''
|
|
196
|
+
return { pathname, params, query, hash }
|
|
197
|
+
}
|
package/.prettierignore
DELETED
package/.prettierrc
DELETED
package/.swc-cjs
DELETED
package/.swc-esm
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{}
|
package/CHANGELOG.md
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
## 0.9.5
|
|
2
|
-
|
|
3
|
-
- Upgrade all depedencies.
|
|
4
|
-
|
|
5
|
-
## 0.9.4
|
|
6
|
-
|
|
7
|
-
- Upgrade all depedencies.
|
|
8
|
-
|
|
9
|
-
## 0.9.3
|
|
10
|
-
|
|
11
|
-
- Upgrade all depedencies.
|
|
12
|
-
|
|
13
|
-
## 0.9.2
|
|
14
|
-
|
|
15
|
-
- Upgrade all depedencies.
|
|
16
|
-
|
|
17
|
-
## 0.9.1
|
|
18
|
-
|
|
19
|
-
- Revert to commonjs (remove `type: "module"`) to keep support for cjs in node and esm in browsers.
|
|
20
|
-
|
|
21
|
-
## 0.9.0
|
|
22
|
-
|
|
23
|
-
- Upgrade all dependencies.
|
|
24
|
-
- Switch from babel to swc for dist compilation.
|
|
25
|
-
|
|
26
|
-
## 0.8.5
|
|
27
|
-
|
|
28
|
-
- fix redirects - the history was not ready by the time initial transition was kicked off by redirecting
|
|
29
|
-
- remove the possibility to pass the current route via the `merge` option directly, instead set `merge: true` (truthy value works now), and instead optionally pass the current route as the second argument to `navigate(to, curr)` or `href(to, curr)`, this is a breaking change, but also an undocumented API as it it's still being refined and is used for correctly merging urls during async navigations.
|
|
30
|
-
|
|
31
|
-
## 0.8.4
|
|
32
|
-
|
|
33
|
-
- fix the `navigate(url: string)` implementation where `"".replace` was being tested instead of `to.replace`
|
|
34
|
-
|
|
35
|
-
## 0.8.3
|
|
36
|
-
|
|
37
|
-
- re-introduce `navigate(url: string)` since it's nice and convenient
|
|
38
|
-
|
|
39
|
-
## 0.8.2
|
|
40
|
-
|
|
41
|
-
- refer to `setImmediate` via `global.setImmediate` to fix a webpack compilation error
|
|
42
|
-
|
|
43
|
-
## 0.8.1
|
|
44
|
-
|
|
45
|
-
- allow passing current route as `merge` value to opt out of using current url and using an externally passed in route, useful in async context where reading current URL might not be safe
|
|
46
|
-
|
|
47
|
-
## 0.8.0
|
|
48
|
-
|
|
49
|
-
A rewrite of space-router. It's better now!
|
|
50
|
-
|
|
51
|
-
- less methods (only `listen`, `navigate`, `match`, `href` and `getUrl`)
|
|
52
|
-
- simpler function signatures and simpler listen callback - you just get the route, not route+data
|
|
53
|
-
- improved route config format with objects (`path`, `redirect`, `routes`) instead of arrays
|
|
54
|
-
- redirects feature
|
|
55
|
-
- new docs at [https://kidkarolis.github.io/space-router/](https://kidkarolis.github.io/space-router/)
|
|
56
|
-
|
|
57
|
-
All in all - it's a new API, but it's spiritually very much the same and is the best version of Space Router yet!
|
|
58
|
-
|
|
59
|
-
## 0.7.0
|
|
60
|
-
|
|
61
|
-
- **Improvement** - Add `replace(url, options)` in addition to `push`, because it matches the DOM API closer and the names are semantically meaningful.
|
|
62
|
-
- **Improvement** - Add `set(options)`, which unlike `push/replace`, can be used to only update specific bits of the url, e.g. `set({ query: { a: 1 } })` would add a=1 into the query string in addition to what's already there. Set `query` or `hash` to `null` to clear them.
|
|
63
|
-
- **Improvement** - Add `match(url)`, which matches the provided url against the route config and returns `{ route, data }`, useful in Server Side Rendering.
|
|
64
|
-
- **Breaking change** - remove `route.path` in favor of the new `route.href`.
|
|
65
|
-
|
|
66
|
-
## 0.6.0
|
|
67
|
-
|
|
68
|
-
- **Improvement** - Add `route.href` in favor of `route.path`, which was too close to `route.pathname` and therefore confusing.
|
package/dist/cjs/history.js
DELETED
|
@@ -1,100 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "createHistory", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return createHistory;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
function createHistory(options) {
|
|
12
|
-
var sync = options.sync;
|
|
13
|
-
var mode = options.mode;
|
|
14
|
-
var raf;
|
|
15
|
-
var onPop;
|
|
16
|
-
var memory = [];
|
|
17
|
-
var off;
|
|
18
|
-
var destroyed = false;
|
|
19
|
-
if (typeof window === 'undefined') {
|
|
20
|
-
mode = 'memory';
|
|
21
|
-
raf = sync ? function(cb) {
|
|
22
|
-
return cb();
|
|
23
|
-
} : global.setImmediate;
|
|
24
|
-
} else {
|
|
25
|
-
raf = sync ? function(cb) {
|
|
26
|
-
return cb();
|
|
27
|
-
} : requestAnimationFrame;
|
|
28
|
-
if (mode === 'history' && !history.pushState) {
|
|
29
|
-
mode = 'hash';
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function listen(onChange) {
|
|
33
|
-
onPop = function onPop() {
|
|
34
|
-
return onChange(getUrl());
|
|
35
|
-
};
|
|
36
|
-
if (mode !== 'memory') {
|
|
37
|
-
off = on(window, mode === 'history' ? 'popstate' : 'hashchange', onPop);
|
|
38
|
-
raf(onPop);
|
|
39
|
-
}
|
|
40
|
-
return function() {
|
|
41
|
-
destroyed = true;
|
|
42
|
-
if (off) off();
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
return {
|
|
46
|
-
listen: listen,
|
|
47
|
-
getUrl: getUrl,
|
|
48
|
-
push: function push(url) {
|
|
49
|
-
go(url);
|
|
50
|
-
},
|
|
51
|
-
replace: function replace(url) {
|
|
52
|
-
go(url, true);
|
|
53
|
-
}
|
|
54
|
-
};
|
|
55
|
-
function go(url, replace) {
|
|
56
|
-
if (destroyed) return;
|
|
57
|
-
url = url.replace(/^\/?#?\/?/, '/').replace(/\/$/, '') || '/';
|
|
58
|
-
if (mode === 'history') {
|
|
59
|
-
history[replace ? 'replaceState' : 'pushState']({}, '', url);
|
|
60
|
-
raf(onPop);
|
|
61
|
-
} else if (mode === 'hash') {
|
|
62
|
-
location[replace ? 'replace' : 'assign']('#' + url);
|
|
63
|
-
} else if (mode === 'memory') {
|
|
64
|
-
if (replace) {
|
|
65
|
-
memory[memory.length - 1] = url;
|
|
66
|
-
} else {
|
|
67
|
-
memory.push(url);
|
|
68
|
-
}
|
|
69
|
-
raf(onPop);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
function getUrl() {
|
|
73
|
-
if (mode === 'memory') {
|
|
74
|
-
return memory[memory.length - 1];
|
|
75
|
-
}
|
|
76
|
-
var hash = getHash();
|
|
77
|
-
if (mode === 'hash') {
|
|
78
|
-
return hash === '' ? '/' : hash;
|
|
79
|
-
}
|
|
80
|
-
if (mode === 'history') {
|
|
81
|
-
var url = location.pathname + location.search;
|
|
82
|
-
if (hash !== '') {
|
|
83
|
-
url += '#' + hash;
|
|
84
|
-
}
|
|
85
|
-
return url;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
// Gets the true hash value. Cannot use location.hash directly due to bug
|
|
89
|
-
// in Firefox where location.hash will always be decoded.
|
|
90
|
-
function getHash() {
|
|
91
|
-
var match = location.href.match(/#(.*)$/);
|
|
92
|
-
return match ? match[1].replace('#', '') : '';
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
function on(el, type, fn) {
|
|
96
|
-
el.addEventListener(type, fn, false);
|
|
97
|
-
return function off() {
|
|
98
|
-
el.removeEventListener(type, fn, false);
|
|
99
|
-
};
|
|
100
|
-
}
|
package/dist/cjs/index.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get createHistory () {
|
|
13
|
-
return _history.createHistory;
|
|
14
|
-
},
|
|
15
|
-
get createRouter () {
|
|
16
|
-
return _router.createRouter;
|
|
17
|
-
},
|
|
18
|
-
get merge () {
|
|
19
|
-
return _router.merge;
|
|
20
|
-
},
|
|
21
|
-
get qs () {
|
|
22
|
-
return _qs.qs;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
var _router = require("./router");
|
|
26
|
-
var _history = require("./history");
|
|
27
|
-
var _qs = require("./qs");
|
package/dist/cjs/match.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
function _export(target, all) {
|
|
6
|
-
for(var name in all)Object.defineProperty(target, name, {
|
|
7
|
-
enumerable: true,
|
|
8
|
-
get: Object.getOwnPropertyDescriptor(all, name).get
|
|
9
|
-
});
|
|
10
|
-
}
|
|
11
|
-
_export(exports, {
|
|
12
|
-
get match () {
|
|
13
|
-
return match;
|
|
14
|
-
},
|
|
15
|
-
get matchOne () {
|
|
16
|
-
return matchOne;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
function match(routes, url, qs) {
|
|
20
|
-
if (!url) {
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
for(var i = 0; i < routes.length; i++){
|
|
24
|
-
var m = matchOne(routes[i].pattern, url, qs);
|
|
25
|
-
if (m) {
|
|
26
|
-
return m;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function matchOne(pattern, url, qs) {
|
|
31
|
-
if (!pattern) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
var re = /(?:\?([^#]*))?(#.*)?$/;
|
|
35
|
-
var originalUrl = url;
|
|
36
|
-
var originalPattern = pattern;
|
|
37
|
-
var c = url.match(re);
|
|
38
|
-
var params = {};
|
|
39
|
-
var query = {};
|
|
40
|
-
var search = '';
|
|
41
|
-
var hash = '';
|
|
42
|
-
var ret;
|
|
43
|
-
if (c && c[1]) {
|
|
44
|
-
search = '?' + c[1];
|
|
45
|
-
query = qs.parse(c[1]);
|
|
46
|
-
}
|
|
47
|
-
if (c && c[2]) {
|
|
48
|
-
hash = c[2] || '';
|
|
49
|
-
}
|
|
50
|
-
if (pattern !== '*') {
|
|
51
|
-
url = segmentize(url.replace(re, ''));
|
|
52
|
-
pattern = segmentize(pattern || '');
|
|
53
|
-
var max = Math.max(url.length, pattern.length);
|
|
54
|
-
for(var i = 0; i < max; i++){
|
|
55
|
-
if (pattern[i] && pattern[i].charAt(0) === ':') {
|
|
56
|
-
var param = pattern[i].replace(/(^:|[+*?]+$)/g, '');
|
|
57
|
-
var flags = (pattern[i].match(/[+*?]+$/) || {})[0] || '';
|
|
58
|
-
var plus = flags.indexOf('+') > -1;
|
|
59
|
-
var star = flags.indexOf('*') > -1;
|
|
60
|
-
var val = url[i] || '';
|
|
61
|
-
if (!val && !star && (flags.indexOf('?') < 0 || plus)) {
|
|
62
|
-
ret = false;
|
|
63
|
-
break;
|
|
64
|
-
}
|
|
65
|
-
params[param] = decodeURIComponent(val);
|
|
66
|
-
if (plus || star) {
|
|
67
|
-
params[param] = url.slice(i).map(decodeURIComponent).join('/');
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
} else if (pattern[i] !== url[i]) {
|
|
71
|
-
ret = false;
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
if (ret === false) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return {
|
|
80
|
-
pattern: originalPattern,
|
|
81
|
-
url: originalUrl,
|
|
82
|
-
pathname: originalUrl.replace(re, ''),
|
|
83
|
-
params: params,
|
|
84
|
-
query: query,
|
|
85
|
-
search: search,
|
|
86
|
-
hash: hash
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
function segmentize(url) {
|
|
90
|
-
return strip(url).split('/');
|
|
91
|
-
}
|
|
92
|
-
function strip(url) {
|
|
93
|
-
return url.replace(/(^\/+|\/+$)/g, '');
|
|
94
|
-
}
|
package/dist/cjs/qs.js
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "qs", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return qs;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
var qs = {
|
|
12
|
-
parse: function parse(queryString) {
|
|
13
|
-
return queryString.split('&').reduce(function(acc, pair) {
|
|
14
|
-
var parts = pair.split('=');
|
|
15
|
-
acc[parts[0]] = decodeURIComponent(parts[1]);
|
|
16
|
-
return acc;
|
|
17
|
-
}, {});
|
|
18
|
-
},
|
|
19
|
-
stringify: function stringify(query) {
|
|
20
|
-
return Object.keys(query).reduce(function(acc, key) {
|
|
21
|
-
if (query[key] !== undefined) {
|
|
22
|
-
acc.push(key + '=' + encodeURIComponent(query[key]));
|
|
23
|
-
}
|
|
24
|
-
return acc;
|
|
25
|
-
}, []).join('&');
|
|
26
|
-
}
|
|
27
|
-
};
|