@ripetchor/r-router 1.0.1 → 1.0.2
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 +2 -2
- package/dist/index.d.ts +76 -11
- package/dist/index.js +340 -287
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -28,7 +28,7 @@ const routes = [
|
|
|
28
28
|
},
|
|
29
29
|
{
|
|
30
30
|
path: '/about',
|
|
31
|
-
|
|
31
|
+
lazy: async () => {
|
|
32
32
|
const { default: About } = await import('./About');
|
|
33
33
|
return About();
|
|
34
34
|
},
|
|
@@ -111,7 +111,7 @@ Navigates, replacing history entry.
|
|
|
111
111
|
### Route Types
|
|
112
112
|
|
|
113
113
|
- `SyncRoute`: Returns `HTMLElement` via `component(ctx: RouteContext)`.
|
|
114
|
-
- `AsyncRoute`: Returns `Promise<HTMLElement>` via `
|
|
114
|
+
- `AsyncRoute`: Returns `Promise<HTMLElement>` via `lazy(ctx: RouteContext)`.
|
|
115
115
|
- `RedirectRoute`: Redirects via `redirectTo`.
|
|
116
116
|
|
|
117
117
|
#### `Route.beforeEnter`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
1
|
export declare type AfterEnterHook = (ctx: RouteContext) => void | Promise<void>;
|
|
2
2
|
|
|
3
|
-
declare interface AsyncRoute extends BaseRoute {
|
|
4
|
-
redirectTo?: never;
|
|
5
|
-
component?(): never;
|
|
6
|
-
loadComponent(ctx: RouteContext): Promise<HTMLElement>;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
3
|
declare interface BaseRoute {
|
|
10
4
|
path: string;
|
|
11
5
|
title?: string | ((ctx: RouteContext) => string);
|
|
@@ -18,36 +12,107 @@ export declare type BeforeEnterHook = (ctx: RouteContext) => boolean | string |
|
|
|
18
12
|
|
|
19
13
|
export declare function createRouter(routes: Route[], options?: RouterOptions): Router;
|
|
20
14
|
|
|
15
|
+
declare interface LazyRoute extends BaseRoute {
|
|
16
|
+
redirectTo?: never;
|
|
17
|
+
component?(): never;
|
|
18
|
+
lazy(ctx: RouteContext): Promise<HTMLElement>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
21
|
declare interface RedirectRoute extends BaseRoute {
|
|
22
22
|
redirectTo: string;
|
|
23
23
|
component?(): never;
|
|
24
|
-
|
|
24
|
+
lazy?(): never;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export declare type Route = SyncRoute |
|
|
27
|
+
export declare type Route = SyncRoute | LazyRoute | RedirectRoute;
|
|
28
28
|
|
|
29
29
|
export declare interface RouteContext {
|
|
30
30
|
from: string | null;
|
|
31
31
|
to: string;
|
|
32
|
-
params: Record<string, string>;
|
|
32
|
+
params: Record<string, string | undefined>;
|
|
33
33
|
query: URLSearchParams;
|
|
34
34
|
signal: AbortSignal;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
export declare class Router {
|
|
38
|
+
static instance: Router | null;
|
|
39
|
+
private readonly dispatcher;
|
|
38
40
|
private readonly routerController;
|
|
39
|
-
private initialized;
|
|
40
41
|
private navigationController;
|
|
42
|
+
private initialized;
|
|
41
43
|
private readonly routeMatchers;
|
|
42
44
|
private readonly options;
|
|
43
45
|
private readonly outlet;
|
|
44
46
|
private currentPath;
|
|
47
|
+
private pendingPath;
|
|
45
48
|
constructor(routes: Route[], options?: RouterOptions);
|
|
49
|
+
on<K extends keyof RouterEventMap>(event: K, handler: RouterDispatcherHandler<RouterEventMap[K]>): () => void;
|
|
50
|
+
private emit;
|
|
46
51
|
initialize(): Promise<Readonly<RouterOutlet>>;
|
|
52
|
+
private handlePopState;
|
|
47
53
|
destroy(): void;
|
|
48
54
|
private navigate;
|
|
55
|
+
private abortNavigation;
|
|
56
|
+
private cancelCurrentNavigation;
|
|
57
|
+
private finalizeNavigation;
|
|
49
58
|
push(path: string, state?: unknown): Promise<void>;
|
|
50
59
|
replace(path: string, state?: unknown): Promise<void>;
|
|
60
|
+
getCurrentPath(): string | null;
|
|
61
|
+
getPendingPath(): string | null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
declare type RouterDispatcherHandler<T> = (payload: T) => void;
|
|
65
|
+
|
|
66
|
+
declare type RouterEventMap = {
|
|
67
|
+
navigationstart: {
|
|
68
|
+
from: string | null;
|
|
69
|
+
to: string;
|
|
70
|
+
mode: "push" | "replace" | "none";
|
|
71
|
+
};
|
|
72
|
+
navigating: {
|
|
73
|
+
from: string | null;
|
|
74
|
+
to: string;
|
|
75
|
+
depth: number;
|
|
76
|
+
};
|
|
77
|
+
navigationend: {
|
|
78
|
+
from: string | null;
|
|
79
|
+
to: string;
|
|
80
|
+
successful: boolean;
|
|
81
|
+
};
|
|
82
|
+
pathchanged: {
|
|
83
|
+
from: string | null;
|
|
84
|
+
to: string;
|
|
85
|
+
};
|
|
86
|
+
querychanged: {
|
|
87
|
+
from: URLSearchParams;
|
|
88
|
+
to: URLSearchParams;
|
|
89
|
+
};
|
|
90
|
+
navigationabort: {
|
|
91
|
+
from: string | null;
|
|
92
|
+
to: string;
|
|
93
|
+
reason: string;
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export declare class RouterLink extends HTMLAnchorElement {
|
|
98
|
+
private isActive;
|
|
99
|
+
private isPending;
|
|
100
|
+
private unsubscribeFns;
|
|
101
|
+
private props;
|
|
102
|
+
constructor(props: RouterLinkProps);
|
|
103
|
+
connectedCallback(): void;
|
|
104
|
+
disconnectedCallback(): void;
|
|
105
|
+
private applyClass;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
declare interface RouterLinkProps {
|
|
109
|
+
to: string;
|
|
110
|
+
className?: string | ((params: {
|
|
111
|
+
isActive: boolean;
|
|
112
|
+
isPending: boolean;
|
|
113
|
+
}) => string);
|
|
114
|
+
textContent: string;
|
|
115
|
+
replace?: boolean;
|
|
51
116
|
}
|
|
52
117
|
|
|
53
118
|
declare interface RouterOptions {
|
|
@@ -68,7 +133,7 @@ declare type RouterOutletProps = {
|
|
|
68
133
|
declare interface SyncRoute extends BaseRoute {
|
|
69
134
|
redirectTo?: never;
|
|
70
135
|
component(ctx: RouteContext): HTMLElement;
|
|
71
|
-
|
|
136
|
+
lazy?(): never;
|
|
72
137
|
}
|
|
73
138
|
|
|
74
139
|
export declare function withAbortSignal<T>(fn: () => Promise<T>, signal: AbortSignal): Promise<T>;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
function
|
|
2
|
-
const
|
|
3
|
-
|
|
1
|
+
function z(e, t) {
|
|
2
|
+
const n = document.createElement("div");
|
|
3
|
+
n.style.cssText = `
|
|
4
4
|
position: fixed;
|
|
5
5
|
inset: 0;
|
|
6
6
|
background-color: rgba(0, 0, 0, 0.5);
|
|
@@ -11,8 +11,8 @@ function P(t, e) {
|
|
|
11
11
|
backdrop-filter: blur(4px);
|
|
12
12
|
animation: fadeIn 0.25s ease;
|
|
13
13
|
`;
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
const o = document.createElement("div");
|
|
15
|
+
o.style.cssText = `
|
|
16
16
|
position: relative;
|
|
17
17
|
max-width: 80dvh;
|
|
18
18
|
width: 100%;
|
|
@@ -33,15 +33,15 @@ function P(t, e) {
|
|
|
33
33
|
color: var(--accent);
|
|
34
34
|
`;
|
|
35
35
|
const i = document.createElement("h2");
|
|
36
|
-
i.textContent =
|
|
36
|
+
i.textContent = e?.message || "An unexpected error occurred.", i.style.cssText = `
|
|
37
37
|
font-size: 1.1rem;
|
|
38
38
|
margin-bottom: 16px;
|
|
39
39
|
opacity: 0.9;
|
|
40
40
|
`;
|
|
41
|
-
const a = document.createElement("div"),
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
41
|
+
const a = document.createElement("div"), f = document.createElement("p"), l = document.createElement("p");
|
|
42
|
+
f.textContent = t?.from ?? "", l.textContent = t?.to ?? "", a.append(f, l);
|
|
43
|
+
const c = document.createElement("pre");
|
|
44
|
+
c.textContent = e?.stack || "", c.style.cssText = `
|
|
45
45
|
font-size: 0.9rem;
|
|
46
46
|
max-height: 30vh;
|
|
47
47
|
overflow: auto;
|
|
@@ -52,8 +52,8 @@ function P(t, e) {
|
|
|
52
52
|
white-space: pre-wrap;
|
|
53
53
|
margin-bottom: 24px;
|
|
54
54
|
`;
|
|
55
|
-
const
|
|
56
|
-
|
|
55
|
+
const s = document.createElement("button");
|
|
56
|
+
s.textContent = "Reload Page", s.style.cssText = `
|
|
57
57
|
cursor: pointer;
|
|
58
58
|
padding: 10px 20px;
|
|
59
59
|
border: none;
|
|
@@ -64,8 +64,8 @@ function P(t, e) {
|
|
|
64
64
|
color: var(--button-text);
|
|
65
65
|
transition: background-color 0.25s;
|
|
66
66
|
`;
|
|
67
|
-
const
|
|
68
|
-
|
|
67
|
+
const u = document.createElement("button");
|
|
68
|
+
u.innerHTML = "✕", u.setAttribute("aria-label", "Close error dialog"), u.style.cssText = `
|
|
69
69
|
position: absolute;
|
|
70
70
|
top: 12px;
|
|
71
71
|
right: 12px;
|
|
@@ -76,391 +76,444 @@ function P(t, e) {
|
|
|
76
76
|
cursor: pointer;
|
|
77
77
|
opacity: 0.7;
|
|
78
78
|
transition: opacity 0.25s;
|
|
79
|
-
`,
|
|
80
|
-
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
},
|
|
86
|
-
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
},
|
|
79
|
+
`, u.onmouseenter = () => {
|
|
80
|
+
u.style.opacity = "1";
|
|
81
|
+
}, u.onmouseleave = () => {
|
|
82
|
+
u.style.opacity = "0.7";
|
|
83
|
+
}, u.onclick = () => {
|
|
84
|
+
n.remove();
|
|
85
|
+
}, s.onmouseenter = () => {
|
|
86
|
+
s.style.backgroundColor = "var(--button-bg-hover)";
|
|
87
|
+
}, s.onmouseleave = () => {
|
|
88
|
+
s.style.backgroundColor = "var(--button-bg)";
|
|
89
|
+
}, s.onclick = () => {
|
|
90
90
|
location.reload();
|
|
91
|
-
},
|
|
92
|
-
const
|
|
93
|
-
|
|
91
|
+
}, n.appendChild(o), o.append(u, r, i, a), e.stack && o.append(c), o.append(s);
|
|
92
|
+
const w = (h) => {
|
|
93
|
+
h.key === "Escape" && n.remove();
|
|
94
94
|
};
|
|
95
|
-
window.addEventListener("keydown",
|
|
96
|
-
const
|
|
97
|
-
document.body.contains(
|
|
95
|
+
window.addEventListener("keydown", w);
|
|
96
|
+
const b = new MutationObserver(() => {
|
|
97
|
+
document.body.contains(n) || (window.removeEventListener("keydown", w), b.disconnect());
|
|
98
98
|
});
|
|
99
|
-
|
|
100
|
-
const
|
|
101
|
-
const
|
|
102
|
-
|
|
99
|
+
b.observe(document.body, { childList: !0 });
|
|
100
|
+
const v = () => {
|
|
101
|
+
const h = window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
102
|
+
o.style.setProperty("--bg", h ? "#2b2b2b" : "#fff"), o.style.setProperty("--text", h ? "#eee" : "#222"), o.style.setProperty("--accent", h ? "#ff6b6b" : "#e74c3c"), o.style.setProperty(
|
|
103
103
|
"--code-bg",
|
|
104
|
-
|
|
105
|
-
),
|
|
104
|
+
h ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.05)"
|
|
105
|
+
), o.style.setProperty("--button-bg", h ? "#3498db55" : "#3498db"), o.style.setProperty(
|
|
106
106
|
"--button-bg-hover",
|
|
107
|
-
|
|
108
|
-
),
|
|
107
|
+
h ? "#3498db88" : "#2980b9"
|
|
108
|
+
), o.style.setProperty("--button-text", "#fff");
|
|
109
109
|
};
|
|
110
|
-
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",
|
|
111
|
-
const
|
|
112
|
-
return
|
|
110
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", v), v();
|
|
111
|
+
const E = document.createElement("style");
|
|
112
|
+
return E.textContent = `
|
|
113
113
|
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
|
|
114
114
|
@keyframes scaleIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
|
115
|
-
`, document.head.appendChild(
|
|
115
|
+
`, document.head.appendChild(E), n;
|
|
116
116
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
});
|
|
117
|
+
function g(e, t) {
|
|
118
|
+
const { promise: n, resolve: o, reject: r } = Promise.withResolvers(), i = new DOMException("Aborted", "AbortError");
|
|
119
|
+
if (t.aborted)
|
|
120
|
+
return r(i), n;
|
|
121
|
+
function a() {
|
|
122
|
+
r(i);
|
|
123
|
+
}
|
|
124
|
+
return t.addEventListener("abort", a, { once: !0 }), e().then(o, r), n;
|
|
126
125
|
}
|
|
127
|
-
function
|
|
128
|
-
const
|
|
129
|
-
return
|
|
126
|
+
function R(e) {
|
|
127
|
+
const t = typeof e == "string" ? e : "Unknown error";
|
|
128
|
+
return e instanceof Error ? e : new Error(t);
|
|
130
129
|
}
|
|
131
|
-
function
|
|
132
|
-
|
|
130
|
+
function S(e, t, n) {
|
|
131
|
+
e.title ? document.title = typeof e.title == "function" ? e.title(t) : e.title : document.title = n ?? "";
|
|
133
132
|
}
|
|
134
|
-
function
|
|
135
|
-
const
|
|
136
|
-
switch (
|
|
133
|
+
function y(e, t, n) {
|
|
134
|
+
const o = t ?? null;
|
|
135
|
+
switch (n) {
|
|
137
136
|
case "push":
|
|
138
|
-
window.history.pushState(
|
|
137
|
+
window.history.pushState(o, "", e);
|
|
139
138
|
break;
|
|
140
139
|
case "replace":
|
|
141
|
-
window.history.replaceState(
|
|
140
|
+
window.history.replaceState(o, "", e);
|
|
142
141
|
break;
|
|
143
142
|
}
|
|
144
143
|
}
|
|
145
|
-
function
|
|
146
|
-
|
|
144
|
+
function T(e, t) {
|
|
145
|
+
e.replaceChildren(t);
|
|
147
146
|
}
|
|
148
|
-
function
|
|
149
|
-
const i =
|
|
150
|
-
|
|
147
|
+
function m(e, t, n, o, r) {
|
|
148
|
+
const i = R(t), a = n?.errorComponent?.(i, o) ?? r?.(i) ?? z(i, o);
|
|
149
|
+
e.replaceChildren(a);
|
|
151
150
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
return t
|
|
151
|
+
function L(e) {
|
|
152
|
+
const [, t] = e.split("?");
|
|
153
|
+
return new URLSearchParams(t || "");
|
|
155
154
|
}
|
|
156
|
-
function
|
|
157
|
-
const
|
|
158
|
-
return new URLSearchParams(e || "");
|
|
159
|
-
}
|
|
160
|
-
function S({ path: t }) {
|
|
161
|
-
const e = t.replace(
|
|
162
|
-
/:([^/]+)/g,
|
|
163
|
-
(o, n) => `(?<${n}>[^/]+)`
|
|
164
|
-
);
|
|
165
|
-
return new RegExp(`^${e}$`);
|
|
166
|
-
}
|
|
167
|
-
function $(t) {
|
|
168
|
-
const e = S(t);
|
|
169
|
-
return {
|
|
170
|
-
route: t,
|
|
171
|
-
redirectTo: t.redirectTo || null,
|
|
172
|
-
match(o) {
|
|
173
|
-
const n = o.split("?")[0];
|
|
174
|
-
return e.test(n);
|
|
175
|
-
},
|
|
176
|
-
getParams(o) {
|
|
177
|
-
return e.exec(o)?.groups ?? {};
|
|
178
|
-
},
|
|
179
|
-
getQuery: x
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
function M({ path: t }) {
|
|
183
|
-
return t === A ? new RegExp("^.*$") : new RegExp(`^${t}$`);
|
|
184
|
-
}
|
|
185
|
-
function W(t) {
|
|
186
|
-
const e = M(t);
|
|
155
|
+
function A(e) {
|
|
156
|
+
const t = new URLPattern({ pathname: e.path });
|
|
187
157
|
return {
|
|
188
|
-
route:
|
|
189
|
-
redirectTo:
|
|
190
|
-
match(
|
|
191
|
-
const
|
|
192
|
-
return
|
|
158
|
+
route: e,
|
|
159
|
+
redirectTo: e.redirectTo || null,
|
|
160
|
+
match(n) {
|
|
161
|
+
const o = n.split("?")[0];
|
|
162
|
+
return t.test({ pathname: o });
|
|
193
163
|
},
|
|
194
|
-
getParams() {
|
|
195
|
-
|
|
164
|
+
getParams(n) {
|
|
165
|
+
const o = n.split("?")[0];
|
|
166
|
+
return t.exec({ pathname: o })?.pathname.groups ?? {};
|
|
196
167
|
},
|
|
197
|
-
getQuery:
|
|
168
|
+
getQuery: L
|
|
198
169
|
};
|
|
199
170
|
}
|
|
200
|
-
|
|
201
|
-
|
|
171
|
+
class d {
|
|
172
|
+
static instance;
|
|
173
|
+
static getInstance() {
|
|
174
|
+
return d.instance || (d.instance = new d()), d.instance;
|
|
175
|
+
}
|
|
176
|
+
listeners = /* @__PURE__ */ new Map();
|
|
177
|
+
on(t, n) {
|
|
178
|
+
let o = this.listeners.get(t);
|
|
179
|
+
return o || (o = /* @__PURE__ */ new Set(), this.listeners.set(t, o)), o.add(n), () => o.delete(n);
|
|
180
|
+
}
|
|
181
|
+
emit(t, n) {
|
|
182
|
+
this.listeners.get(t)?.forEach((o) => o(n));
|
|
183
|
+
}
|
|
184
|
+
clear() {
|
|
185
|
+
this.listeners.clear();
|
|
186
|
+
}
|
|
202
187
|
}
|
|
203
|
-
function
|
|
204
|
-
for (let
|
|
205
|
-
if (
|
|
206
|
-
return
|
|
188
|
+
function N(e, t) {
|
|
189
|
+
for (let n = 0; n < t.length; n++)
|
|
190
|
+
if (t[n].match(e))
|
|
191
|
+
return t[n];
|
|
207
192
|
return null;
|
|
208
193
|
}
|
|
209
|
-
async function
|
|
210
|
-
return typeof
|
|
194
|
+
async function M(e, t) {
|
|
195
|
+
return typeof e.component == "function" ? e.component(t) : e.lazy && typeof e.lazy == "function" ? await e.lazy(t) : new Error("Failed to get component for route: " + e.path);
|
|
211
196
|
}
|
|
212
|
-
async function
|
|
213
|
-
const
|
|
214
|
-
if (!(!
|
|
215
|
-
for (const
|
|
216
|
-
await
|
|
197
|
+
async function $(e, t) {
|
|
198
|
+
const n = e.afterEnter;
|
|
199
|
+
if (!(!n || n.length === 0))
|
|
200
|
+
for (const o of n)
|
|
201
|
+
await o(t);
|
|
217
202
|
}
|
|
218
|
-
async function
|
|
219
|
-
const
|
|
220
|
-
if (!
|
|
203
|
+
async function U(e, t) {
|
|
204
|
+
const n = e.beforeEnter;
|
|
205
|
+
if (!n || n.length === 0)
|
|
221
206
|
return null;
|
|
222
|
-
for (const
|
|
223
|
-
|
|
207
|
+
for (const o of n) {
|
|
208
|
+
if (t.signal.aborted)
|
|
209
|
+
return !1;
|
|
210
|
+
const r = await o(t);
|
|
224
211
|
if (r === !1 || typeof r == "string")
|
|
225
212
|
return r;
|
|
226
213
|
}
|
|
227
214
|
return null;
|
|
228
215
|
}
|
|
229
|
-
function
|
|
230
|
-
return
|
|
216
|
+
function C(e) {
|
|
217
|
+
return e instanceof DOMException && e.name === "AbortError";
|
|
231
218
|
}
|
|
232
|
-
function
|
|
233
|
-
const
|
|
234
|
-
if (
|
|
219
|
+
function x(e) {
|
|
220
|
+
const t = e.split("/").filter(Boolean);
|
|
221
|
+
if (t.length === 0)
|
|
235
222
|
return 0;
|
|
236
|
-
let
|
|
237
|
-
for (let
|
|
238
|
-
const r =
|
|
239
|
-
r === "*" ?
|
|
223
|
+
let n = 0;
|
|
224
|
+
for (let o = 0; o < t.length; o++) {
|
|
225
|
+
const r = t[o];
|
|
226
|
+
r === "*" ? n -= 100 : r.startsWith(":") ? n += 5 : n += 10;
|
|
240
227
|
}
|
|
241
|
-
return
|
|
228
|
+
return n += t.length * 0.1, n;
|
|
242
229
|
}
|
|
243
|
-
function
|
|
244
|
-
return
|
|
230
|
+
function B(e) {
|
|
231
|
+
return e.toSorted((t, n) => x(n.path) - x(t.path));
|
|
245
232
|
}
|
|
246
|
-
function
|
|
247
|
-
if (
|
|
233
|
+
function I(e, t) {
|
|
234
|
+
if (e === null)
|
|
248
235
|
return !1;
|
|
249
|
-
const
|
|
250
|
-
return !(
|
|
236
|
+
const n = new URL(e, location.origin), o = new URL(t, location.origin);
|
|
237
|
+
return !(n.pathname !== o.pathname || n.search !== o.search);
|
|
251
238
|
}
|
|
252
|
-
class
|
|
253
|
-
constructor(
|
|
254
|
-
super(), this.setAttribute("outlet-id",
|
|
239
|
+
class P extends HTMLElement {
|
|
240
|
+
constructor(t) {
|
|
241
|
+
super(), this.setAttribute("outlet-id", t.outletId);
|
|
255
242
|
}
|
|
256
243
|
}
|
|
257
|
-
customElements.define("router-outlet",
|
|
258
|
-
function
|
|
259
|
-
if (typeof
|
|
260
|
-
throw new Error(`Path must be a string, got ${typeof
|
|
261
|
-
if (
|
|
244
|
+
customElements.define("router-outlet", P);
|
|
245
|
+
function k(e) {
|
|
246
|
+
if (typeof e != "string")
|
|
247
|
+
throw new Error(`Path must be a string, got ${typeof e}`);
|
|
248
|
+
if (e.trim() === "")
|
|
262
249
|
throw new Error("Path cannot be an empty string");
|
|
263
|
-
if (
|
|
264
|
-
throw new Error(`Path must start with '/': "${
|
|
265
|
-
if (
|
|
266
|
-
throw new Error(`Path must not end with '/': "${
|
|
267
|
-
if (
|
|
268
|
-
throw new Error(
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
throw new Error(
|
|
275
|
-
`Path contains invalid characters: "${e.join("")}" in "${t}"`
|
|
276
|
-
);
|
|
250
|
+
if (e !== "*" && !e.startsWith("/"))
|
|
251
|
+
throw new Error(`Path must start with '/': "${e}"`);
|
|
252
|
+
if (e.length > 1 && e.endsWith("/"))
|
|
253
|
+
throw new Error(`Path must not end with '/': "${e}"`);
|
|
254
|
+
if (e.includes("//"))
|
|
255
|
+
throw new Error(`Path must not contain multiple consecutive slashes: "${e}"`);
|
|
256
|
+
if (e === "*")
|
|
257
|
+
return;
|
|
258
|
+
const t = e.match(/[^A-Za-z0-9\-._~:/]/g);
|
|
259
|
+
if (t)
|
|
260
|
+
throw new Error(`Path contains invalid characters: "${t.join("")}" in "${e}"`);
|
|
277
261
|
}
|
|
278
|
-
function
|
|
279
|
-
const
|
|
280
|
-
for (let
|
|
281
|
-
const
|
|
282
|
-
if (
|
|
283
|
-
throw new Error(`Duplicate root route path: "${
|
|
284
|
-
|
|
262
|
+
function q(e) {
|
|
263
|
+
const t = /* @__PURE__ */ new Set();
|
|
264
|
+
for (let n = 0; n < e.length; n++) {
|
|
265
|
+
const o = e[n].path;
|
|
266
|
+
if (t.has(o))
|
|
267
|
+
throw new Error(`Duplicate root route path: "${o}"`);
|
|
268
|
+
t.add(o);
|
|
285
269
|
}
|
|
286
270
|
}
|
|
287
|
-
function
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
271
|
+
function W(e, t) {
|
|
272
|
+
const n = e.split("/"), o = t.split("/");
|
|
273
|
+
return n.length !== o.length ? !1 : n.every((r, i) => {
|
|
274
|
+
const a = o[i];
|
|
275
|
+
return r === a || r.startsWith(":") || a.startsWith(":");
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
function F(e) {
|
|
279
|
+
for (let t = 0; t < e.length; t++)
|
|
280
|
+
for (let n = t + 1; n < e.length; n++)
|
|
281
|
+
if (W(e[t].path, e[n].path))
|
|
282
|
+
throw new Error(`Conflicting routes: "${e[t].path}" and "${e[n].path}"`);
|
|
283
|
+
}
|
|
284
|
+
function H(e) {
|
|
285
|
+
if (!(e.redirectTo === null || e.redirectTo == null) && typeof e.redirectTo == "string") {
|
|
286
|
+
const t = e.redirectTo;
|
|
287
|
+
if (k(t), t === e.path)
|
|
288
|
+
throw new Error(`Route "${e.path}" cannot redirect to itself`);
|
|
289
|
+
if (e.path === "*")
|
|
293
290
|
throw new Error('Catch-all route "*" cannot have a redirect');
|
|
294
291
|
}
|
|
295
292
|
}
|
|
296
|
-
function
|
|
297
|
-
const
|
|
298
|
-
if (
|
|
299
|
-
throw new Error(
|
|
300
|
-
|
|
301
|
-
);
|
|
302
|
-
if (
|
|
303
|
-
throw new Error(
|
|
304
|
-
`Route "${t.path}" cannot have both component and loadComponent`
|
|
305
|
-
);
|
|
306
|
-
if (!e && !o && !n)
|
|
307
|
-
throw new Error(
|
|
308
|
-
`Route "${t.path}" must define either "component" or "loadComponent"`
|
|
309
|
-
);
|
|
293
|
+
function Q(e) {
|
|
294
|
+
const t = typeof e.redirectTo == "string", n = typeof e.component == "function", o = typeof e.lazy == "function";
|
|
295
|
+
if (t && (n || o))
|
|
296
|
+
throw new Error(`Redirect route "${e.path}" must not define components`);
|
|
297
|
+
if (n && o)
|
|
298
|
+
throw new Error(`Route "${e.path}" cannot have both "component" and "lazy"`);
|
|
299
|
+
if (!t && !n && !o)
|
|
300
|
+
throw new Error(`Route "${e.path}" must define either "component" or "lazy"`);
|
|
310
301
|
}
|
|
311
|
-
function
|
|
312
|
-
|
|
302
|
+
function D(e) {
|
|
303
|
+
if (e.path === "*") return;
|
|
304
|
+
const t = e.path.match(/:([^/]+)/g);
|
|
305
|
+
if (!t) return;
|
|
306
|
+
const n = t.map((r) => r.slice(1));
|
|
307
|
+
if (new Set(n).size !== n.length)
|
|
308
|
+
throw new Error(`Duplicate parameter names in path "${e.path}"`);
|
|
313
309
|
}
|
|
314
|
-
function
|
|
315
|
-
|
|
316
|
-
for (let e = 0; e < t.length; e++) {
|
|
317
|
-
const o = t[e];
|
|
318
|
-
j(o);
|
|
319
|
-
}
|
|
310
|
+
function O(e) {
|
|
311
|
+
k(e.path), H(e), Q(e), D(e);
|
|
320
312
|
}
|
|
321
|
-
|
|
313
|
+
function j(e) {
|
|
314
|
+
q(e), F(e);
|
|
315
|
+
for (let t = 0; t < e.length; t++)
|
|
316
|
+
O(e[t]);
|
|
317
|
+
}
|
|
318
|
+
class p {
|
|
319
|
+
static instance = null;
|
|
320
|
+
dispatcher = d.getInstance();
|
|
322
321
|
routerController = new AbortController();
|
|
323
|
-
initialized = !1;
|
|
324
322
|
navigationController = null;
|
|
325
|
-
|
|
323
|
+
initialized = !1;
|
|
324
|
+
routeMatchers;
|
|
326
325
|
options;
|
|
327
|
-
outlet = new
|
|
326
|
+
outlet = new P({ outletId: "root" });
|
|
328
327
|
currentPath = null;
|
|
329
|
-
|
|
330
|
-
|
|
328
|
+
pendingPath = null;
|
|
329
|
+
constructor(t, n) {
|
|
330
|
+
j(t), this.options = {
|
|
331
331
|
defaultTitle: document.title,
|
|
332
332
|
sortRoutesBySpecificity: !0,
|
|
333
333
|
ignoreSameLocation: !0,
|
|
334
|
-
...
|
|
334
|
+
...n
|
|
335
335
|
};
|
|
336
|
-
const
|
|
337
|
-
this.routeMatchers =
|
|
336
|
+
const o = this.options.sortRoutesBySpecificity ? B(t) : t;
|
|
337
|
+
this.routeMatchers = o.map(A);
|
|
338
|
+
}
|
|
339
|
+
on(t, n) {
|
|
340
|
+
return this.dispatcher.on(t, n);
|
|
341
|
+
}
|
|
342
|
+
emit(t, n) {
|
|
343
|
+
this.dispatcher.emit(t, n);
|
|
338
344
|
}
|
|
339
345
|
async initialize() {
|
|
340
346
|
if (this.initialized)
|
|
341
347
|
throw new Error("Router already initialized");
|
|
342
|
-
|
|
343
|
-
const r = window.location.pathname + window.location.search;
|
|
344
|
-
await this.navigate(r, n.state, "none");
|
|
345
|
-
};
|
|
346
|
-
window.addEventListener(
|
|
348
|
+
p.instance = this, window.addEventListener(
|
|
347
349
|
"popstate",
|
|
348
350
|
(n) => {
|
|
349
|
-
|
|
351
|
+
this.handlePopState(n).catch(console.error);
|
|
350
352
|
},
|
|
351
|
-
{
|
|
353
|
+
{
|
|
354
|
+
signal: this.routerController.signal
|
|
355
|
+
}
|
|
352
356
|
);
|
|
353
|
-
const
|
|
354
|
-
return await this.navigate(
|
|
357
|
+
const t = window.location.pathname + window.location.search;
|
|
358
|
+
return await this.navigate(t, null, "none"), this.initialized = !0, this.outlet;
|
|
359
|
+
}
|
|
360
|
+
async handlePopState(t) {
|
|
361
|
+
const n = window.location.pathname + window.location.search;
|
|
362
|
+
await this.navigate(n, t.state, "none");
|
|
355
363
|
}
|
|
356
364
|
destroy() {
|
|
357
365
|
if (!this.initialized)
|
|
358
366
|
throw new Error("Router not initialized");
|
|
359
|
-
this.routerController.abort(), this.navigationController
|
|
367
|
+
this.routerController.abort(), this.navigationController?.abort(), this.navigationController = null, this.dispatcher.clear(), this.initialized = !1, p.instance = null;
|
|
360
368
|
}
|
|
361
|
-
async navigate(
|
|
362
|
-
if (r > 5)
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
369
|
+
async navigate(t, n, o, r = 0) {
|
|
370
|
+
if (r === 0 && (this.emit("navigationstart", { from: this.currentPath, to: t, mode: o }), this.pendingPath = t), this.emit("navigating", { from: this.currentPath, to: t, depth: r }), r > 5)
|
|
371
|
+
return this.abortNavigation(t, "too many redirects", new Error("Too many redirects"));
|
|
372
|
+
if (this.options.ignoreSameLocation && I(this.currentPath, t))
|
|
373
|
+
return this.pendingPath = null, this.abortNavigation(t, "same location");
|
|
374
|
+
this.cancelCurrentNavigation(), this.navigationController = new AbortController();
|
|
375
|
+
const i = this.navigationController.signal;
|
|
376
|
+
i.addEventListener(
|
|
377
|
+
"abort",
|
|
378
|
+
() => this.emit("navigationabort", {
|
|
379
|
+
from: this.currentPath,
|
|
380
|
+
to: t,
|
|
381
|
+
reason: String(i.reason ?? "aborted")
|
|
382
|
+
})
|
|
383
|
+
);
|
|
384
|
+
const a = N(t, this.routeMatchers), f = this.currentPath, l = {
|
|
385
|
+
from: f,
|
|
386
|
+
to: t,
|
|
387
|
+
params: a?.getParams(t) ?? {},
|
|
388
|
+
query: a?.getQuery(t) ?? new URLSearchParams(),
|
|
380
389
|
signal: i
|
|
381
390
|
};
|
|
382
391
|
try {
|
|
383
|
-
if (!a)
|
|
384
|
-
|
|
392
|
+
if (!a)
|
|
393
|
+
return y(t, n, o), m(
|
|
385
394
|
this.outlet,
|
|
386
395
|
new Error('Route not found. Add "*" catch-all route'),
|
|
387
396
|
null,
|
|
388
|
-
|
|
397
|
+
l,
|
|
389
398
|
this.options.globalErrorComponent
|
|
390
|
-
), this.
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
if (a.redirectTo) {
|
|
394
|
-
await this.navigate(a.redirectTo, o, "replace", r + 1);
|
|
395
|
-
return;
|
|
396
|
-
}
|
|
399
|
+
), this.finalizeNavigation(t, f, !1);
|
|
400
|
+
if (a.redirectTo)
|
|
401
|
+
return this.navigate(a.redirectTo, n, "replace", r + 1);
|
|
397
402
|
if (i.aborted)
|
|
398
403
|
return;
|
|
399
|
-
const c = await
|
|
400
|
-
() =>
|
|
404
|
+
const c = await g(
|
|
405
|
+
() => U(a.route, l),
|
|
401
406
|
i
|
|
402
407
|
);
|
|
403
408
|
if (c === !1)
|
|
404
409
|
return;
|
|
405
|
-
if (typeof c == "string")
|
|
406
|
-
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
410
|
+
if (typeof c == "string")
|
|
411
|
+
return this.navigate(c, n, "replace", r + 1);
|
|
409
412
|
if (i.aborted)
|
|
410
413
|
return;
|
|
411
|
-
const
|
|
412
|
-
() =>
|
|
414
|
+
const s = await g(
|
|
415
|
+
() => M(a.route, l),
|
|
413
416
|
i
|
|
414
417
|
);
|
|
415
|
-
if (
|
|
416
|
-
|
|
417
|
-
return;
|
|
418
|
-
m(e, o, n), h(
|
|
418
|
+
if (s instanceof Error)
|
|
419
|
+
return C(s) ? void 0 : (y(t, n, o), m(
|
|
419
420
|
this.outlet,
|
|
420
|
-
d,
|
|
421
|
-
a.route,
|
|
422
421
|
s,
|
|
422
|
+
a.route,
|
|
423
|
+
l,
|
|
423
424
|
this.options.globalErrorComponent
|
|
424
|
-
), this.
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
425
|
+
), this.finalizeNavigation(t, f, !1));
|
|
427
426
|
if (i.aborted)
|
|
428
427
|
return;
|
|
429
|
-
|
|
428
|
+
T(this.outlet, s), y(t, n, o), S(a.route, l, this.options.defaultTitle), this.finalizeNavigation(t, f, !0), await g(() => $(a.route, l), i);
|
|
430
429
|
} catch (c) {
|
|
431
|
-
if (
|
|
430
|
+
if (C(c))
|
|
432
431
|
return;
|
|
433
|
-
|
|
432
|
+
m(
|
|
434
433
|
this.outlet,
|
|
435
434
|
c instanceof Error ? c : new Error("Unknown error"),
|
|
436
435
|
a?.route ?? null,
|
|
437
|
-
|
|
436
|
+
l,
|
|
438
437
|
this.options.globalErrorComponent
|
|
439
|
-
);
|
|
438
|
+
), this.emit("navigationend", { from: this.currentPath, to: t, successful: !1 });
|
|
440
439
|
} finally {
|
|
441
440
|
this.navigationController?.signal === i && (this.navigationController = null);
|
|
442
441
|
}
|
|
443
442
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
await this.navigate(e, o, "push");
|
|
447
|
-
} catch (n) {
|
|
448
|
-
console.error("[Router.push] Error:", n);
|
|
449
|
-
}
|
|
443
|
+
abortNavigation(t, n, o) {
|
|
444
|
+
this.pendingPath = null, this.emit("navigationabort", { from: this.currentPath, to: t, reason: n }), o && m(this.outlet, o, null, null, this.options.globalErrorComponent);
|
|
450
445
|
}
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
446
|
+
cancelCurrentNavigation() {
|
|
447
|
+
this.navigationController && (this.navigationController.abort("navigation canceled"), this.navigationController = null);
|
|
448
|
+
}
|
|
449
|
+
finalizeNavigation(t, n, o) {
|
|
450
|
+
const r = n ? new URLSearchParams(n.split("?")[1] ?? "") : null, i = new URLSearchParams(t.split("?")[1] ?? "");
|
|
451
|
+
this.currentPath = t, this.pendingPath = null, this.emit("pathchanged", { from: n, to: t }), (!r || r?.toString() !== i.toString()) && this.emit("querychanged", { from: r ?? new URLSearchParams(), to: i }), this.emit("navigationend", { from: n, to: t, successful: o });
|
|
457
452
|
}
|
|
453
|
+
async push(t, n) {
|
|
454
|
+
await this.navigate(t, n, "push");
|
|
455
|
+
}
|
|
456
|
+
async replace(t, n) {
|
|
457
|
+
await this.navigate(t, n, "replace");
|
|
458
|
+
}
|
|
459
|
+
getCurrentPath() {
|
|
460
|
+
return this.currentPath;
|
|
461
|
+
}
|
|
462
|
+
getPendingPath() {
|
|
463
|
+
return this.pendingPath;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
function G(e, t) {
|
|
467
|
+
return new p(e, t);
|
|
458
468
|
}
|
|
459
|
-
|
|
460
|
-
|
|
469
|
+
class K extends HTMLAnchorElement {
|
|
470
|
+
isActive = !1;
|
|
471
|
+
isPending = !1;
|
|
472
|
+
unsubscribeFns = [];
|
|
473
|
+
props;
|
|
474
|
+
constructor(t) {
|
|
475
|
+
super(), this.props = t, this.href = t.to, this.textContent = t.textContent, this.addEventListener("click", (n) => {
|
|
476
|
+
n.preventDefault();
|
|
477
|
+
const o = p.instance;
|
|
478
|
+
t.replace ? o?.replace(t.to) : o?.push(t.to);
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
connectedCallback() {
|
|
482
|
+
const t = p.instance;
|
|
483
|
+
if (!t) return;
|
|
484
|
+
const n = () => {
|
|
485
|
+
const o = t.getCurrentPath() || "", r = this.props.to;
|
|
486
|
+
this.isActive = o.split("?")[0] === r.split("?")[0], this.isPending = t.getPendingPath() === r, this.applyClass();
|
|
487
|
+
};
|
|
488
|
+
n(), this.unsubscribeFns.push(
|
|
489
|
+
t.on("navigationstart", n),
|
|
490
|
+
t.on("navigating", n),
|
|
491
|
+
t.on("navigationend", n),
|
|
492
|
+
t.on("navigationabort", n),
|
|
493
|
+
t.on("pathchanged", n),
|
|
494
|
+
t.on("querychanged", n)
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
disconnectedCallback() {
|
|
498
|
+
this.unsubscribeFns.forEach((t) => t()), this.unsubscribeFns = [];
|
|
499
|
+
}
|
|
500
|
+
applyClass() {
|
|
501
|
+
if (this.props.className) {
|
|
502
|
+
if (typeof this.props.className == "string") {
|
|
503
|
+
this.className = this.props.className;
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
this.className = this.props.className({
|
|
507
|
+
isActive: this.isActive,
|
|
508
|
+
isPending: this.isPending
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
461
512
|
}
|
|
513
|
+
customElements.define("router-link", K, { extends: "a" });
|
|
462
514
|
export {
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
515
|
+
p as Router,
|
|
516
|
+
K as RouterLink,
|
|
517
|
+
G as createRouter,
|
|
518
|
+
g as withAbortSignal
|
|
466
519
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripetchor/r-router",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"files": [
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@eslint/js": "^9.37.0",
|
|
27
|
+
"@types/node": "^25.0.3",
|
|
27
28
|
"eslint": "^9.37.0",
|
|
28
29
|
"typescript": "~5.9.3",
|
|
29
30
|
"typescript-eslint": "^8.45.0",
|