space-router 1.0.0 → 1.1.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 -1
- package/dist/history.js +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/match.d.ts +0 -5
- package/dist/match.js +0 -9
- package/dist/router.d.ts +15 -4
- package/dist/router.js +29 -19
- package/package.json +7 -7
- package/src/history.ts +2 -2
- package/src/index.ts +13 -2
- package/src/match.ts +0 -12
- package/src/router.ts +56 -30
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<h4 align="center">Framework agnostic router for single page apps</h4>
|
|
7
7
|
<br />
|
|
8
8
|
|
|
9
|
-
Space Router packs all the features you need to keep your app in sync with the url. It's distinct from many other routers in that there is only **a single callback**. This callback can be used to re-render your
|
|
9
|
+
Space Router packs all the features you need to keep your app in sync with the url. It's distinct from many other routers in that there is only **a single callback**. This callback can be used to re-render your application, update a store and perform other actions on each url change. Space Router is **stateless**, it doesn't store the current route leaving state completely up to you to handle.
|
|
10
10
|
|
|
11
11
|
In summary, Space Router:
|
|
12
12
|
|
package/dist/history.js
CHANGED
|
@@ -53,7 +53,7 @@ export function createHistory(options = {}) {
|
|
|
53
53
|
schedule();
|
|
54
54
|
}
|
|
55
55
|
else if (mode === 'memory') {
|
|
56
|
-
if (replace) {
|
|
56
|
+
if (replace && memory.length) {
|
|
57
57
|
memory[memory.length - 1] = url;
|
|
58
58
|
}
|
|
59
59
|
else {
|
|
@@ -64,7 +64,7 @@ export function createHistory(options = {}) {
|
|
|
64
64
|
}
|
|
65
65
|
function getUrl() {
|
|
66
66
|
if (mode === 'memory') {
|
|
67
|
-
return memory[memory.length - 1];
|
|
67
|
+
return memory[memory.length - 1] ?? '';
|
|
68
68
|
}
|
|
69
69
|
const hash = getHash();
|
|
70
70
|
if (mode === 'hash') {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { qs } from './qs.ts';
|
|
2
2
|
export { createHistory } from './history.ts';
|
|
3
|
-
export { createRouter, merge } from './router.ts';
|
|
3
|
+
export { createMatcher, createRouter, merge } from './router.ts';
|
|
4
4
|
export type { Qs } from './qs.ts';
|
|
5
5
|
export type { Mode, History, CreateHistoryOptions } from './history.ts';
|
|
6
|
-
export type { RouterOptions, Route, NavigateTarget, To, Redirect, RouteDefinition, Router } from './router.ts';
|
|
6
|
+
export type { MatcherOptions, RouteData, RouterOptions, Route, NavigateTarget, To, Redirect, RouteDefinition, Matcher, Router, } from './router.ts';
|
package/dist/index.js
CHANGED
package/dist/match.d.ts
CHANGED
|
@@ -8,9 +8,4 @@ export interface MatchedRoute {
|
|
|
8
8
|
search: string;
|
|
9
9
|
hash: string;
|
|
10
10
|
}
|
|
11
|
-
interface RouteEntry {
|
|
12
|
-
pattern: string;
|
|
13
|
-
}
|
|
14
|
-
export declare function match(routes: RouteEntry[], url: string | undefined, qs: Qs): MatchedRoute | undefined;
|
|
15
11
|
export declare function matchOne(pattern: string, url: string, qs?: Qs): MatchedRoute | undefined;
|
|
16
|
-
export {};
|
package/dist/match.js
CHANGED
|
@@ -1,12 +1,3 @@
|
|
|
1
|
-
export function match(routes, url, qs) {
|
|
2
|
-
if (!url)
|
|
3
|
-
return;
|
|
4
|
-
for (let i = 0; i < routes.length; i++) {
|
|
5
|
-
const m = matchOne(routes[i].pattern, url, qs);
|
|
6
|
-
if (m)
|
|
7
|
-
return m;
|
|
8
|
-
}
|
|
9
|
-
}
|
|
10
1
|
export function matchOne(pattern, url, qs) {
|
|
11
2
|
if (!pattern)
|
|
12
3
|
return;
|
package/dist/router.d.ts
CHANGED
|
@@ -6,8 +6,11 @@ export interface RouterOptions {
|
|
|
6
6
|
qs?: Qs;
|
|
7
7
|
sync?: boolean;
|
|
8
8
|
}
|
|
9
|
+
export interface MatcherOptions {
|
|
10
|
+
qs?: Qs;
|
|
11
|
+
}
|
|
9
12
|
export interface Route<Data = Record<string, unknown>> extends MatchedRoute {
|
|
10
|
-
data: Data[];
|
|
13
|
+
data: RouteData<Data>[];
|
|
11
14
|
}
|
|
12
15
|
export interface NavigateTarget {
|
|
13
16
|
url?: string;
|
|
@@ -20,6 +23,10 @@ export interface NavigateTarget {
|
|
|
20
23
|
}
|
|
21
24
|
export type To = string | NavigateTarget;
|
|
22
25
|
export type Redirect<Data = Record<string, unknown>> = To | ((route: Route<Data>) => To);
|
|
26
|
+
export type RouteData<Data = Record<string, unknown>> = Data & {
|
|
27
|
+
path: string;
|
|
28
|
+
redirect?: Redirect<Data>;
|
|
29
|
+
};
|
|
23
30
|
export type RouteDefinition<Data = Record<string, unknown>> = Data & {
|
|
24
31
|
path?: string;
|
|
25
32
|
redirect?: Redirect<Data>;
|
|
@@ -32,11 +39,15 @@ export interface Router<Data = Record<string, unknown>> {
|
|
|
32
39
|
match(url: string): Route<Data> | undefined;
|
|
33
40
|
getUrl(): string;
|
|
34
41
|
}
|
|
35
|
-
interface
|
|
42
|
+
export interface Matcher<Data = Record<string, unknown>> {
|
|
43
|
+
match(url: string): Route<Data> | undefined;
|
|
44
|
+
}
|
|
45
|
+
interface FlatRoute<Data = Record<string, unknown>> {
|
|
36
46
|
pattern: string;
|
|
37
|
-
data:
|
|
47
|
+
data: RouteData<Data>[];
|
|
38
48
|
}
|
|
39
49
|
export declare function createRouter<Data = Record<string, unknown>>(options?: RouterOptions): Router<Data>;
|
|
40
|
-
export declare function
|
|
50
|
+
export declare function createMatcher<Data = Record<string, unknown>>(routeMap: RouteDefinition<Data>[], options?: MatcherOptions): Matcher<Data>;
|
|
51
|
+
export declare function flatten<Data = Record<string, unknown>>(routeMap: RouteDefinition<Data>[]): FlatRoute<Data>[];
|
|
41
52
|
export declare function merge(curr: Partial<Route> | NavigateTarget | undefined, to: NavigateTarget): NavigateTarget;
|
|
42
53
|
export {};
|
package/dist/router.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { matchOne } from "./match.js";
|
|
2
2
|
import { createHistory } from "./history.js";
|
|
3
3
|
import { qs as defaultQs } from "./qs.js";
|
|
4
4
|
const PARAM_RE = /:([A-Za-z0-9_]+)([+*?])?/g;
|
|
@@ -8,13 +8,13 @@ export function createRouter(options = {}) {
|
|
|
8
8
|
const qs = options.qs || defaultQs;
|
|
9
9
|
const sync = options.sync || false;
|
|
10
10
|
const history = createHistory({ mode, sync });
|
|
11
|
-
let
|
|
11
|
+
let matcher = createMatcher([], { qs });
|
|
12
12
|
const router = {
|
|
13
13
|
listen(routeMap, cb) {
|
|
14
|
-
|
|
14
|
+
matcher = createMatcher(routeMap, { qs });
|
|
15
15
|
let redirects = 0;
|
|
16
16
|
return history.listen((url) => {
|
|
17
|
-
const route =
|
|
17
|
+
const route = matcher.match(url);
|
|
18
18
|
if (!route) {
|
|
19
19
|
redirects = 0;
|
|
20
20
|
return;
|
|
@@ -84,11 +84,7 @@ export function createRouter(options = {}) {
|
|
|
84
84
|
return url;
|
|
85
85
|
},
|
|
86
86
|
match(url) {
|
|
87
|
-
|
|
88
|
-
if (route) {
|
|
89
|
-
return { ...route, data: data(routes, route) };
|
|
90
|
-
}
|
|
91
|
-
return undefined;
|
|
87
|
+
return matcher.match(url);
|
|
92
88
|
},
|
|
93
89
|
getUrl() {
|
|
94
90
|
return history.getUrl();
|
|
@@ -96,15 +92,37 @@ export function createRouter(options = {}) {
|
|
|
96
92
|
};
|
|
97
93
|
return router;
|
|
98
94
|
}
|
|
95
|
+
export function createMatcher(routeMap, options = {}) {
|
|
96
|
+
const routes = flatten(routeMap);
|
|
97
|
+
const qs = options.qs || defaultQs;
|
|
98
|
+
return {
|
|
99
|
+
match(url) {
|
|
100
|
+
if (!url)
|
|
101
|
+
return undefined;
|
|
102
|
+
for (const route of routes) {
|
|
103
|
+
const m = matchOne(route.pattern, url, qs);
|
|
104
|
+
if (m) {
|
|
105
|
+
return { ...m, data: route.data };
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return undefined;
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
99
112
|
export function flatten(routeMap) {
|
|
100
113
|
const routes = [];
|
|
101
114
|
const parentData = [];
|
|
102
115
|
function addLevel(level) {
|
|
103
116
|
level.forEach((route) => {
|
|
104
117
|
const { path = '', routes: children, ...routeData } = route;
|
|
105
|
-
|
|
118
|
+
const segment = { path, ...routeData };
|
|
119
|
+
// pathless routes only contribute data to their children — they have
|
|
120
|
+
// no pattern of their own to match
|
|
121
|
+
if (path) {
|
|
122
|
+
routes.push({ pattern: path, data: parentData.concat([segment]) });
|
|
123
|
+
}
|
|
106
124
|
if (children) {
|
|
107
|
-
parentData.push(
|
|
125
|
+
parentData.push(segment);
|
|
108
126
|
addLevel(children);
|
|
109
127
|
parentData.pop();
|
|
110
128
|
}
|
|
@@ -113,14 +131,6 @@ export function flatten(routeMap) {
|
|
|
113
131
|
addLevel(routeMap);
|
|
114
132
|
return routes;
|
|
115
133
|
}
|
|
116
|
-
function data(routes, matchingRoute) {
|
|
117
|
-
for (let i = 0; i < routes.length; i++) {
|
|
118
|
-
if (routes[i].pattern === matchingRoute.pattern) {
|
|
119
|
-
return routes[i].data;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return [];
|
|
123
|
-
}
|
|
124
134
|
export function merge(curr, to) {
|
|
125
135
|
const c = (curr || {});
|
|
126
136
|
const pathname = to.pathname || c.pattern || c.pathname;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "space-router",
|
|
3
3
|
"description": "All the routing essentials.",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.1.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -47,16 +47,16 @@
|
|
|
47
47
|
"release:docs": "hugo -s docs && gh-pages -d docs/public"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"ava": "^
|
|
50
|
+
"ava": "^8.0.1",
|
|
51
51
|
"c8": "^11.0.0",
|
|
52
52
|
"gh-pages": "^6.3.0",
|
|
53
|
-
"oxfmt": "^0.
|
|
54
|
-
"oxlint": "^1.
|
|
53
|
+
"oxfmt": "^0.57.0",
|
|
54
|
+
"oxlint": "^1.72.0",
|
|
55
55
|
"typescript": "^6.0.3"
|
|
56
56
|
},
|
|
57
57
|
"ava": {
|
|
58
|
-
"extensions":
|
|
59
|
-
"ts"
|
|
60
|
-
|
|
58
|
+
"extensions": [
|
|
59
|
+
"ts"
|
|
60
|
+
]
|
|
61
61
|
}
|
|
62
62
|
}
|
package/src/history.ts
CHANGED
|
@@ -65,7 +65,7 @@ export function createHistory(options: CreateHistoryOptions = {}): History {
|
|
|
65
65
|
location[replace ? 'replace' : 'assign']('#' + url)
|
|
66
66
|
if (same) schedule()
|
|
67
67
|
} else if (mode === 'memory') {
|
|
68
|
-
if (replace) {
|
|
68
|
+
if (replace && memory.length) {
|
|
69
69
|
memory[memory.length - 1] = url
|
|
70
70
|
} else {
|
|
71
71
|
memory.push(url)
|
|
@@ -76,7 +76,7 @@ export function createHistory(options: CreateHistoryOptions = {}): History {
|
|
|
76
76
|
|
|
77
77
|
function getUrl(): string {
|
|
78
78
|
if (mode === 'memory') {
|
|
79
|
-
return memory[memory.length - 1]
|
|
79
|
+
return memory[memory.length - 1] ?? ''
|
|
80
80
|
}
|
|
81
81
|
|
|
82
82
|
const hash = getHash()
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
export { qs } from './qs.ts'
|
|
2
2
|
export { createHistory } from './history.ts'
|
|
3
|
-
export { createRouter, merge } from './router.ts'
|
|
3
|
+
export { createMatcher, createRouter, merge } from './router.ts'
|
|
4
4
|
|
|
5
5
|
export type { Qs } from './qs.ts'
|
|
6
6
|
export type { Mode, History, CreateHistoryOptions } from './history.ts'
|
|
7
|
-
export type {
|
|
7
|
+
export type {
|
|
8
|
+
MatcherOptions,
|
|
9
|
+
RouteData,
|
|
10
|
+
RouterOptions,
|
|
11
|
+
Route,
|
|
12
|
+
NavigateTarget,
|
|
13
|
+
To,
|
|
14
|
+
Redirect,
|
|
15
|
+
RouteDefinition,
|
|
16
|
+
Matcher,
|
|
17
|
+
Router,
|
|
18
|
+
} from './router.ts'
|
package/src/match.ts
CHANGED
|
@@ -10,18 +10,6 @@ export interface MatchedRoute {
|
|
|
10
10
|
hash: string
|
|
11
11
|
}
|
|
12
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
13
|
export function matchOne(pattern: string, url: string, qs?: Qs): MatchedRoute | undefined {
|
|
26
14
|
if (!pattern) return
|
|
27
15
|
|
package/src/router.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { matchOne, type MatchedRoute } from './match.ts'
|
|
2
2
|
import { createHistory, type Mode } from './history.ts'
|
|
3
3
|
import { qs as defaultQs, type Qs } from './qs.ts'
|
|
4
4
|
|
|
@@ -8,8 +8,12 @@ export interface RouterOptions {
|
|
|
8
8
|
sync?: boolean
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
export interface MatcherOptions {
|
|
12
|
+
qs?: Qs
|
|
13
|
+
}
|
|
14
|
+
|
|
11
15
|
export interface Route<Data = Record<string, unknown>> extends MatchedRoute {
|
|
12
|
-
data: Data[]
|
|
16
|
+
data: RouteData<Data>[]
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
export interface NavigateTarget {
|
|
@@ -26,6 +30,11 @@ export type To = string | NavigateTarget
|
|
|
26
30
|
|
|
27
31
|
export type Redirect<Data = Record<string, unknown>> = To | ((route: Route<Data>) => To)
|
|
28
32
|
|
|
33
|
+
export type RouteData<Data = Record<string, unknown>> = Data & {
|
|
34
|
+
path: string
|
|
35
|
+
redirect?: Redirect<Data>
|
|
36
|
+
}
|
|
37
|
+
|
|
29
38
|
export type RouteDefinition<Data = Record<string, unknown>> = Data & {
|
|
30
39
|
path?: string
|
|
31
40
|
redirect?: Redirect<Data>
|
|
@@ -40,9 +49,13 @@ export interface Router<Data = Record<string, unknown>> {
|
|
|
40
49
|
getUrl(): string
|
|
41
50
|
}
|
|
42
51
|
|
|
43
|
-
interface
|
|
52
|
+
export interface Matcher<Data = Record<string, unknown>> {
|
|
53
|
+
match(url: string): Route<Data> | undefined
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface FlatRoute<Data = Record<string, unknown>> {
|
|
44
57
|
pattern: string
|
|
45
|
-
data:
|
|
58
|
+
data: RouteData<Data>[]
|
|
46
59
|
}
|
|
47
60
|
|
|
48
61
|
const PARAM_RE = /:([A-Za-z0-9_]+)([+*?])?/g
|
|
@@ -54,19 +67,19 @@ export function createRouter<Data = Record<string, unknown>>(options: RouterOpti
|
|
|
54
67
|
const sync = options.sync || false
|
|
55
68
|
const history = createHistory({ mode, sync })
|
|
56
69
|
|
|
57
|
-
let
|
|
70
|
+
let matcher = createMatcher<Data>([], { qs })
|
|
58
71
|
|
|
59
72
|
const router: Router<Data> = {
|
|
60
73
|
listen(routeMap, cb) {
|
|
61
|
-
|
|
74
|
+
matcher = createMatcher(routeMap, { qs })
|
|
62
75
|
let redirects = 0
|
|
63
76
|
return history.listen((url) => {
|
|
64
|
-
const route =
|
|
77
|
+
const route = matcher.match(url)
|
|
65
78
|
if (!route) {
|
|
66
79
|
redirects = 0
|
|
67
80
|
return
|
|
68
81
|
}
|
|
69
|
-
for (const r of route.data
|
|
82
|
+
for (const r of route.data) {
|
|
70
83
|
if (r.redirect) {
|
|
71
84
|
if (++redirects > MAX_REDIRECTS) {
|
|
72
85
|
redirects = 0
|
|
@@ -138,11 +151,7 @@ export function createRouter<Data = Record<string, unknown>>(options: RouterOpti
|
|
|
138
151
|
},
|
|
139
152
|
|
|
140
153
|
match(url) {
|
|
141
|
-
|
|
142
|
-
if (route) {
|
|
143
|
-
return { ...route, data: data(routes, route) } as Route<Data>
|
|
144
|
-
}
|
|
145
|
-
return undefined
|
|
154
|
+
return matcher.match(url)
|
|
146
155
|
},
|
|
147
156
|
|
|
148
157
|
getUrl() {
|
|
@@ -153,22 +162,48 @@ export function createRouter<Data = Record<string, unknown>>(options: RouterOpti
|
|
|
153
162
|
return router
|
|
154
163
|
}
|
|
155
164
|
|
|
156
|
-
export function
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
165
|
+
export function createMatcher<Data = Record<string, unknown>>(
|
|
166
|
+
routeMap: RouteDefinition<Data>[],
|
|
167
|
+
options: MatcherOptions = {},
|
|
168
|
+
): Matcher<Data> {
|
|
169
|
+
const routes = flatten(routeMap)
|
|
170
|
+
const qs = options.qs || defaultQs
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
match(url) {
|
|
174
|
+
if (!url) return undefined
|
|
175
|
+
for (const route of routes) {
|
|
176
|
+
const m = matchOne(route.pattern, url, qs)
|
|
177
|
+
if (m) {
|
|
178
|
+
return { ...m, data: route.data } as Route<Data>
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return undefined
|
|
182
|
+
},
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export function flatten<Data = Record<string, unknown>>(routeMap: RouteDefinition<Data>[]): FlatRoute<Data>[] {
|
|
187
|
+
const routes: FlatRoute<Data>[] = []
|
|
188
|
+
const parentData: RouteData<Data>[] = []
|
|
189
|
+
function addLevel(level: RouteDefinition<Data>[]) {
|
|
160
190
|
level.forEach((route) => {
|
|
161
191
|
const {
|
|
162
192
|
path = '',
|
|
163
193
|
routes: children,
|
|
164
194
|
...routeData
|
|
165
|
-
} = route as RouteDefinition & {
|
|
195
|
+
} = route as RouteDefinition<Data> & {
|
|
166
196
|
path?: string
|
|
167
|
-
routes?: RouteDefinition[]
|
|
197
|
+
routes?: RouteDefinition<Data>[]
|
|
198
|
+
}
|
|
199
|
+
const segment = { path, ...routeData } as RouteData<Data>
|
|
200
|
+
// pathless routes only contribute data to their children — they have
|
|
201
|
+
// no pattern of their own to match
|
|
202
|
+
if (path) {
|
|
203
|
+
routes.push({ pattern: path, data: parentData.concat([segment]) })
|
|
168
204
|
}
|
|
169
|
-
routes.push({ pattern: path, data: parentData.concat([routeData]) })
|
|
170
205
|
if (children) {
|
|
171
|
-
parentData.push(
|
|
206
|
+
parentData.push(segment)
|
|
172
207
|
addLevel(children)
|
|
173
208
|
parentData.pop()
|
|
174
209
|
}
|
|
@@ -178,15 +213,6 @@ export function flatten(routeMap: RouteDefinition[]): FlatRoute[] {
|
|
|
178
213
|
return routes
|
|
179
214
|
}
|
|
180
215
|
|
|
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
216
|
export function merge(curr: Partial<Route> | NavigateTarget | undefined, to: NavigateTarget): NavigateTarget {
|
|
191
217
|
const c = (curr || {}) as Partial<Route> & NavigateTarget
|
|
192
218
|
const pathname = to.pathname || c.pattern || c.pathname
|