@praxisjs/router 0.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/CHANGELOG.md +14 -0
- package/LICENSE +21 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +3 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/link.d.ts +11 -0
- package/dist/components/link.d.ts.map +1 -0
- package/dist/components/link.js +18 -0
- package/dist/components/link.js.map +1 -0
- package/dist/components/router-view.d.ts +3 -0
- package/dist/components/router-view.d.ts.map +1 -0
- package/dist/components/router-view.js +10 -0
- package/dist/components/router-view.js.map +1 -0
- package/dist/decorators.d.ts +2 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +12 -0
- package/dist/decorators.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/router.d.ts +37 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +152 -0
- package/dist/router.js.map +1 -0
- package/dist/types/route.d.ts +27 -0
- package/dist/types/route.d.ts.map +1 -0
- package/dist/types/route.js +2 -0
- package/dist/types/route.js.map +1 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +26 -0
- package/dist/utils.js.map +1 -0
- package/package.json +25 -0
- package/src/components/index.ts +2 -0
- package/src/components/link.tsx +45 -0
- package/src/components/router-view.tsx +16 -0
- package/src/decorators.ts +11 -0
- package/src/index.ts +20 -0
- package/src/router.ts +221 -0
- package/src/types/route.ts +37 -0
- package/src/utils.ts +34 -0
- package/tsconfig.json +9 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Mateus Martins
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Children, VNode } from "@praxisjs/shared";
|
|
2
|
+
interface LinkProps {
|
|
3
|
+
to: string;
|
|
4
|
+
replace?: boolean;
|
|
5
|
+
class?: string;
|
|
6
|
+
activeClass?: string;
|
|
7
|
+
children?: Children | Children[];
|
|
8
|
+
}
|
|
9
|
+
export declare function Link({ to, replace, class: cls, activeClass, children, }: LinkProps): VNode;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.d.ts","sourceRoot":"","sources":["../../src/components/link.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAIxD,UAAU,SAAS;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,EAAE,CAAC;CAClC;AAED,wBAAgB,IAAI,CAAC,EACnB,EAAE,EACF,OAAe,EACf,KAAK,EAAE,GAAQ,EACf,WAAsB,EACtB,QAAQ,GACT,EAAE,SAAS,GAAG,KAAK,CAyBnB"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@praxisjs/jsx/jsx-runtime";
|
|
2
|
+
import { computed } from "@praxisjs/core";
|
|
3
|
+
import { useRouter } from "../router";
|
|
4
|
+
export function Link({ to, replace = false, class: cls = "", activeClass = "active", children, }) {
|
|
5
|
+
const router = useRouter();
|
|
6
|
+
const isActive = computed(() => router.location().path === to);
|
|
7
|
+
const handleClick = (e) => {
|
|
8
|
+
e.preventDefault();
|
|
9
|
+
if (replace) {
|
|
10
|
+
void router.replace(to);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
void router.push(to);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
return (_jsx("a", { href: to, class: () => [cls, isActive() ? activeClass : ""].filter(Boolean).join(" "), onClick: handleClick, children: children }));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link.js","sourceRoot":"","sources":["../../src/components/link.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAG1C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAUtC,MAAM,UAAU,IAAI,CAAC,EACnB,EAAE,EACF,OAAO,GAAG,KAAK,EACf,KAAK,EAAE,GAAG,GAAG,EAAE,EACf,WAAW,GAAG,QAAQ,EACtB,QAAQ,GACE;IACV,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,MAAM,QAAQ,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;IAE/D,MAAM,WAAW,GAAG,CAAC,CAAa,EAAE,EAAE;QACpC,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1B,CAAC;aAAM,CAAC;YACN,KAAK,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC;IAEF,OAAO,CACL,YACE,IAAI,EAAE,EAAE,EACR,KAAK,EAAE,GAAG,EAAE,CACV,CAAC,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAEhE,OAAO,EAAE,WAAW,YAEnB,QAAQ,GACP,CACL,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router-view.d.ts","sourceRoot":"","sources":["../../src/components/router-view.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AAI9C,wBAAgB,UAAU,IAAI,KAAK,CAWlC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { jsx as _jsx } from "@praxisjs/jsx/jsx-runtime";
|
|
2
|
+
import { useRouter } from "../router";
|
|
3
|
+
export function RouterView() {
|
|
4
|
+
const router = useRouter();
|
|
5
|
+
return (_jsx("div", { "data-router-view": "true", children: () => {
|
|
6
|
+
const Component = router.currentComponent();
|
|
7
|
+
return Component ? _jsx(Component, {}) : null;
|
|
8
|
+
} }));
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=router-view.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router-view.js","sourceRoot":"","sources":["../../src/components/router-view.tsx"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,MAAM,UAAU,UAAU;IACxB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,OAAO,CACL,kCAAsB,MAAM,YACzB,GAAG,EAAE;YACJ,MAAM,SAAS,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC5C,OAAO,SAAS,CAAC,CAAC,CAAC,KAAC,SAAS,KAAG,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1C,CAAC,GACG,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.d.ts","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,IAEf,CAAC,SAAS,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,EAAE,aAAa,CAAC,KAAG,CAAC,CAQ3E"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function Route(path) {
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
|
+
return function (constructor) {
|
|
4
|
+
Object.defineProperty(constructor, "__routePath", {
|
|
5
|
+
value: path,
|
|
6
|
+
writable: false,
|
|
7
|
+
configurable: false,
|
|
8
|
+
});
|
|
9
|
+
return constructor;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=decorators.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../src/decorators.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,KAAK,CAAC,IAAY;IAChC,8DAA8D;IAC9D,OAAO,UAAiD,WAAc;QACpE,MAAM,CAAC,cAAc,CAAC,WAAW,EAAE,aAAa,EAAE;YAChD,KAAK,EAAE,IAAI;YACX,QAAQ,EAAE,KAAK;YACf,YAAY,EAAE,KAAK;SACpB,CAAC,CAAC;QACH,OAAO,WAAW,CAAC;IACrB,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createRouter, lazy, useRouter, useParams, useQuery, useLocation, Router, } from "./router";
|
|
2
|
+
export type { RouteDefinition, RouteLocation, RouteParams, RouteQuery, RouteComponent, LazyRouteComponent, } from "./types/route";
|
|
3
|
+
export { RouterView, Link } from "./components";
|
|
4
|
+
export { Route } from "./decorators";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,MAAM,GACP,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,eAAe,EACf,aAAa,EACb,WAAW,EACX,UAAU,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,eAAe,CAAC;AAEvB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,IAAI,EACJ,SAAS,EACT,SAAS,EACT,QAAQ,EACR,WAAW,EACX,MAAM,GACP,MAAM,UAAU,CAAC;AAUlB,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/router.d.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Signal, Computed } from "@praxisjs/shared";
|
|
2
|
+
import type { LazyRouteComponent, RouteComponent, RouteDefinition, RouteLocation, RouteParams, RouteQuery } from "./types/route";
|
|
3
|
+
export declare class Router {
|
|
4
|
+
private readonly compiled;
|
|
5
|
+
private readonly _location;
|
|
6
|
+
private _prevLocation;
|
|
7
|
+
private readonly _component;
|
|
8
|
+
private readonly _loading;
|
|
9
|
+
private readonly _resolvedComponents;
|
|
10
|
+
readonly location: Signal<RouteLocation>;
|
|
11
|
+
readonly currentComponent: Signal<RouteComponent | null>;
|
|
12
|
+
readonly loading: Signal<boolean>;
|
|
13
|
+
readonly params: Computed<RouteParams>;
|
|
14
|
+
readonly query: Computed<RouteQuery>;
|
|
15
|
+
constructor(routes: RouteDefinition[]);
|
|
16
|
+
private addRoute;
|
|
17
|
+
private buildLocation;
|
|
18
|
+
private matchParams;
|
|
19
|
+
private isLazy;
|
|
20
|
+
private resolveComponent;
|
|
21
|
+
private resolveAndSetComponent;
|
|
22
|
+
private syncFromBrowser;
|
|
23
|
+
push(path: string, query?: RouteQuery, hash?: string): Promise<void>;
|
|
24
|
+
replace(path: string, query?: RouteQuery): Promise<void>;
|
|
25
|
+
back(): void;
|
|
26
|
+
forward(): void;
|
|
27
|
+
go(delta: number): void;
|
|
28
|
+
}
|
|
29
|
+
export declare function lazy(loader: () => Promise<{
|
|
30
|
+
default: new (...args: any[]) => any;
|
|
31
|
+
}>): LazyRouteComponent;
|
|
32
|
+
export declare function createRouter(routes: RouteDefinition[]): Router;
|
|
33
|
+
export declare function useRouter(): Router;
|
|
34
|
+
export declare function useParams(): Computed<RouteParams>;
|
|
35
|
+
export declare function useQuery(): Computed<RouteQuery>;
|
|
36
|
+
export declare function useLocation(): Signal<RouteLocation>;
|
|
37
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAIzD,OAAO,KAAK,EAEV,kBAAkB,EAClB,cAAc,EACd,eAAe,EACf,aAAa,EACb,WAAW,EACX,UAAU,EACX,MAAM,eAAe,CAAC;AAEvB,qBAAa,MAAM;IACjB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAuB;IAChD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAwB;IAClD,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAgC;IAC3D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkB;IAC3C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAGhC;IAEJ,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;IACzC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAClC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,CAAC;gBAEzB,MAAM,EAAE,eAAe,EAAE;IA4BrC,OAAO,CAAC,QAAQ;IAYhB,OAAO,CAAC,aAAa;IAcrB,OAAO,CAAC,WAAW;IAanB,OAAO,CAAC,MAAM;YAMA,gBAAgB;YAuBhB,sBAAsB;YAKtB,eAAe;IAUvB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBpE,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9D,IAAI,IAAI,IAAI;IAGZ,OAAO,IAAI,IAAI;IAIf,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;CAGxB;AAKD,wBAAgB,IAAI,CAElB,MAAM,EAAE,MAAM,OAAO,CAAC;IAAE,OAAO,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;CAAE,CAAC,GAC9D,kBAAkB,CAEpB;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,CAG9D;AAED,wBAAgB,SAAS,IAAI,MAAM,CAGlC;AAED,wBAAgB,SAAS,IAAI,QAAQ,CAAC,WAAW,CAAC,CAEjD;AAED,wBAAgB,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,CAE/C;AAED,wBAAgB,WAAW,IAAI,MAAM,CAAC,aAAa,CAAC,CAEnD"}
|
package/dist/router.js
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { computed, signal } from "@praxisjs/core";
|
|
2
|
+
import { compilePath, parseQuery } from "./utils";
|
|
3
|
+
export class Router {
|
|
4
|
+
constructor(routes) {
|
|
5
|
+
this.compiled = [];
|
|
6
|
+
this._prevLocation = null;
|
|
7
|
+
this._resolvedComponents = new Map();
|
|
8
|
+
for (const route of routes) {
|
|
9
|
+
this.addRoute(route);
|
|
10
|
+
}
|
|
11
|
+
const initial = this.buildLocation(window.location.pathname, window.location.search, window.location.hash);
|
|
12
|
+
this._location = signal(initial);
|
|
13
|
+
this._loading = signal(false);
|
|
14
|
+
this._component = signal(null);
|
|
15
|
+
this.location = this._location;
|
|
16
|
+
this.currentComponent = this._component;
|
|
17
|
+
this.loading = this._loading;
|
|
18
|
+
this.params = computed(() => this._location().params);
|
|
19
|
+
this.query = computed(() => this._location().query);
|
|
20
|
+
void this.resolveAndSetComponent(initial.path);
|
|
21
|
+
window.addEventListener("popstate", () => {
|
|
22
|
+
void this.syncFromBrowser();
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
addRoute(route, prefix = "") {
|
|
26
|
+
const fullPath = prefix + route.path;
|
|
27
|
+
const { regex, paramNames } = compilePath(fullPath);
|
|
28
|
+
this.compiled.push({ definition: route, regex, paramNames });
|
|
29
|
+
if (route.children) {
|
|
30
|
+
for (const child of route.children) {
|
|
31
|
+
this.addRoute(child, fullPath === "/" ? "" : fullPath);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
buildLocation(pathname, search, hash) {
|
|
36
|
+
const params = this.matchParams(pathname);
|
|
37
|
+
return {
|
|
38
|
+
path: pathname,
|
|
39
|
+
params,
|
|
40
|
+
query: parseQuery(search),
|
|
41
|
+
hash: hash.replace("#", ""),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
matchParams(path) {
|
|
45
|
+
for (const route of this.compiled) {
|
|
46
|
+
const match = route.regex.exec(path);
|
|
47
|
+
if (!match)
|
|
48
|
+
continue;
|
|
49
|
+
const params = {};
|
|
50
|
+
route.paramNames.forEach((name, i) => {
|
|
51
|
+
params[name] = match[i + 1] ?? "";
|
|
52
|
+
});
|
|
53
|
+
return params;
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
isLazy(c) {
|
|
58
|
+
return "__isLazy" in c && (c).__isLazy;
|
|
59
|
+
}
|
|
60
|
+
async resolveComponent(path) {
|
|
61
|
+
for (const route of this.compiled) {
|
|
62
|
+
if (!route.regex.test(path))
|
|
63
|
+
continue;
|
|
64
|
+
const { component } = route.definition;
|
|
65
|
+
if (!this.isLazy(component))
|
|
66
|
+
return component;
|
|
67
|
+
const cached = this._resolvedComponents.get(component);
|
|
68
|
+
if (cached)
|
|
69
|
+
return cached;
|
|
70
|
+
this._loading.set(true);
|
|
71
|
+
try {
|
|
72
|
+
const mod = await component();
|
|
73
|
+
this._resolvedComponents.set(component, mod.default);
|
|
74
|
+
return mod.default;
|
|
75
|
+
}
|
|
76
|
+
finally {
|
|
77
|
+
this._loading.set(false);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
async resolveAndSetComponent(path) {
|
|
83
|
+
const component = await this.resolveComponent(path);
|
|
84
|
+
this._component.set(component);
|
|
85
|
+
}
|
|
86
|
+
async syncFromBrowser() {
|
|
87
|
+
const loc = this.buildLocation(window.location.pathname, window.location.search, window.location.hash);
|
|
88
|
+
this._location.set(loc);
|
|
89
|
+
await this.resolveAndSetComponent(loc.path);
|
|
90
|
+
}
|
|
91
|
+
async push(path, query, hash) {
|
|
92
|
+
const search = query ? "?" + new URLSearchParams(query).toString() : "";
|
|
93
|
+
const hashStr = hash ? `#${hash}` : "";
|
|
94
|
+
const fullUrl = path + search + hashStr;
|
|
95
|
+
const loc = this.buildLocation(path, search, hashStr);
|
|
96
|
+
const matched = this.compiled.find((r) => r.regex.test(path));
|
|
97
|
+
if (matched?.definition.beforeEnter) {
|
|
98
|
+
const result = await matched.definition.beforeEnter(loc, this._prevLocation);
|
|
99
|
+
if (result === false)
|
|
100
|
+
return;
|
|
101
|
+
if (typeof result === "string") {
|
|
102
|
+
return this.push(result);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
this._prevLocation = this._location();
|
|
106
|
+
window.history.pushState(null, "", fullUrl);
|
|
107
|
+
this._location.set(loc);
|
|
108
|
+
await this.resolveAndSetComponent(path);
|
|
109
|
+
}
|
|
110
|
+
async replace(path, query) {
|
|
111
|
+
const search = query ? "?" + new URLSearchParams(query).toString() : "";
|
|
112
|
+
const loc = this.buildLocation(path, search, "");
|
|
113
|
+
this._prevLocation = this._location();
|
|
114
|
+
window.history.replaceState(null, "", path + search);
|
|
115
|
+
this._location.set(loc);
|
|
116
|
+
await this.resolveAndSetComponent(path);
|
|
117
|
+
}
|
|
118
|
+
back() {
|
|
119
|
+
window.history.back();
|
|
120
|
+
}
|
|
121
|
+
forward() {
|
|
122
|
+
window.history.forward();
|
|
123
|
+
}
|
|
124
|
+
go(delta) {
|
|
125
|
+
window.history.go(delta);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
let _router = null;
|
|
129
|
+
export function lazy(
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
131
|
+
loader) {
|
|
132
|
+
return Object.assign(() => loader(), { __isLazy: true });
|
|
133
|
+
}
|
|
134
|
+
export function createRouter(routes) {
|
|
135
|
+
_router = new Router(routes);
|
|
136
|
+
return _router;
|
|
137
|
+
}
|
|
138
|
+
export function useRouter() {
|
|
139
|
+
if (!_router)
|
|
140
|
+
throw new Error("[Router] createRouter() was not called.");
|
|
141
|
+
return _router;
|
|
142
|
+
}
|
|
143
|
+
export function useParams() {
|
|
144
|
+
return useRouter().params;
|
|
145
|
+
}
|
|
146
|
+
export function useQuery() {
|
|
147
|
+
return useRouter().query;
|
|
148
|
+
}
|
|
149
|
+
export function useLocation() {
|
|
150
|
+
return useRouter().location;
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAYlD,MAAM,OAAO,MAAM;IAiBjB,YAAY,MAAyB;QAhBpB,aAAQ,GAAoB,EAAE,CAAC;QAExC,kBAAa,GAAyB,IAAI,CAAC;QAGlC,wBAAmB,GAAG,IAAI,GAAG,EAG3C,CAAC;QASF,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAChC,MAAM,CAAC,QAAQ,CAAC,QAAQ,EACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,EACtB,MAAM,CAAC,QAAQ,CAAC,IAAI,CACrB,CAAC;QACF,IAAI,CAAC,SAAS,GAAG,MAAM,CAAgB,OAAO,CAAC,CAAC;QAChD,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAU,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAwB,IAAI,CAAC,CAAC;QAEtD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC;QAC/B,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,UAAU,CAAC;QACxC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;QAE7B,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,MAAM,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,KAAK,CAAC,CAAC;QAEpD,KAAK,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAE/C,MAAM,CAAC,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE;YACvC,KAAK,IAAI,CAAC,eAAe,EAAE,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,QAAQ,CAAC,KAAsB,EAAE,MAAM,GAAG,EAAE;QAClD,MAAM,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC;QACrC,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC;QACpD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,CAAC;QAE7D,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACnB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YACzD,CAAC;QACH,CAAC;IACH,CAAC;IAEO,aAAa,CACnB,QAAgB,EAChB,MAAc,EACd,IAAY;QAEZ,MAAM,MAAM,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC1C,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,MAAM;YACN,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC;YACzB,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC;SAC5B,CAAC;IACJ,CAAC;IAEO,WAAW,CAAC,IAAY;QAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,CAAC,KAAK;gBAAE,SAAS;YACrB,MAAM,MAAM,GAAgB,EAAE,CAAC;YAC/B,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE;gBACnC,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;YACpC,CAAC,CAAC,CAAC;YACH,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,EAAE,CAAC;IACZ,CAAC;IAEO,MAAM,CACZ,CAAsC;QAEtC,OAAO,UAAU,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IACzC,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,IAAY;QAEZ,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;gBAAE,SAAS;YACtC,MAAM,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC,UAAU,CAAC;YACvC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC;gBAAE,OAAO,SAAS,CAAC;YAE9C,MAAM,MAAM,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YACvD,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC;YAE1B,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,SAAS,EAAE,CAAC;gBAC9B,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;gBACrD,OAAO,GAAG,CAAC,OAAO,CAAC;YACrB,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,sBAAsB,CAAC,IAAY;QAC/C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IACjC,CAAC;IAEO,KAAK,CAAC,eAAe;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAC5B,MAAM,CAAC,QAAQ,CAAC,QAAQ,EACxB,MAAM,CAAC,QAAQ,CAAC,MAAM,EACtB,MAAM,CAAC,QAAQ,CAAC,IAAI,CACrB,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,MAAM,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,KAAkB,EAAE,IAAa;QACxD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,CAAC;QAExC,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QAEtD,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9D,IAAI,OAAO,EAAE,UAAU,CAAC,WAAW,EAAE,CAAC;YACpC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,WAAW,CACjD,GAAG,EACH,IAAI,CAAC,aAAa,CACnB,CAAC;YACF,IAAI,MAAM,KAAK,KAAK;gBAAE,OAAO;YAC7B,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;gBAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QAED,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,EAAE,EAAE,EAAE,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,MAAM,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,IAAY,EAAE,KAAkB;QAC5C,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,GAAG,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,MAAM,GAAG,GAAG,IAAI,CAAC,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QAEjD,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,SAAS,EAAE,CAAC;QACtC,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;QACrD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxB,MAAM,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,IAAI;QACF,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IACD,OAAO;QACL,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,EAAE,CAAC,KAAa;QACd,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;IAC3B,CAAC;CACF;AAED,IAAI,OAAO,GAAkB,IAAI,CAAC;AAGlC,MAAM,UAAU,IAAI;AAClB,8DAA8D;AAC9D,MAA+D;IAE/D,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,IAAa,EAAE,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAyB;IACpD,OAAO,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC;IAC7B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACzE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,SAAS;IACvB,OAAO,SAAS,EAAE,CAAC,MAAM,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,QAAQ;IACtB,OAAO,SAAS,EAAE,CAAC,KAAK,CAAC;AAC3B,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,OAAO,SAAS,EAAE,CAAC,QAAQ,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type RouteParams = Record<string, string>;
|
|
2
|
+
export type RouteQuery = Record<string, string>;
|
|
3
|
+
export interface RouteLocation {
|
|
4
|
+
path: string;
|
|
5
|
+
params: RouteParams;
|
|
6
|
+
query: RouteQuery;
|
|
7
|
+
hash: string;
|
|
8
|
+
}
|
|
9
|
+
export type RouteComponent = (new (...args: any[]) => any) | ((...args: any[]) => any);
|
|
10
|
+
export interface LazyRouteComponent {
|
|
11
|
+
(): Promise<{
|
|
12
|
+
default: new (...args: any[]) => any;
|
|
13
|
+
}>;
|
|
14
|
+
readonly __isLazy: true;
|
|
15
|
+
}
|
|
16
|
+
export interface RouteDefinition {
|
|
17
|
+
path: string;
|
|
18
|
+
component: RouteComponent | LazyRouteComponent;
|
|
19
|
+
children?: RouteDefinition[];
|
|
20
|
+
beforeEnter?: (to: RouteLocation, from: RouteLocation | null) => boolean | string | Promise<boolean | string>;
|
|
21
|
+
}
|
|
22
|
+
export interface CompiledRoute {
|
|
23
|
+
definition: RouteDefinition;
|
|
24
|
+
regex: RegExp;
|
|
25
|
+
paramNames: string[];
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=route.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.d.ts","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AACjD,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,CAAC;IACpB,KAAK,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,MAAM,cAAc,GAEtB,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,GAE7B,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAC,CAAC;AAE9B,MAAM,WAAW,kBAAkB;IAEjC,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,GAAG,CAAA;KAAE,CAAC,CAAC;IACtD,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,cAAc,GAAG,kBAAkB,CAAC;IAC/C,QAAQ,CAAC,EAAE,eAAe,EAAE,CAAC;IAC7B,WAAW,CAAC,EAAE,CACZ,EAAE,EAAE,aAAa,EACjB,IAAI,EAAE,aAAa,GAAG,IAAI,KACvB,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,GAAG,MAAM,CAAC,CAAC;CACnD;AAED,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,eAAe,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route.js","sourceRoot":"","sources":["../../src/types/route.ts"],"names":[],"mappings":""}
|
package/dist/utils.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB,CAgBA;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAUrD"}
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function compilePath(path) {
|
|
2
|
+
const paramNames = [];
|
|
3
|
+
// /users/:id/posts/:postId → /users/([^/]+)/posts/([^/]+)
|
|
4
|
+
// /docs/** → /docs/(.*)
|
|
5
|
+
const regexStr = path
|
|
6
|
+
.replace(/\*\*/g, "(.*)")
|
|
7
|
+
.replace(/:([^/]+)/g, (_, name) => {
|
|
8
|
+
paramNames.push(name);
|
|
9
|
+
return "([^/]+)";
|
|
10
|
+
});
|
|
11
|
+
return {
|
|
12
|
+
regex: new RegExp(`^${regexStr}$`),
|
|
13
|
+
paramNames,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function parseQuery(search) {
|
|
17
|
+
const query = {};
|
|
18
|
+
if (!search || search === "?")
|
|
19
|
+
return query;
|
|
20
|
+
const params = new URLSearchParams(search.startsWith("?") ? search.slice(1) : search);
|
|
21
|
+
params.forEach((value, key) => {
|
|
22
|
+
query[key] = value;
|
|
23
|
+
});
|
|
24
|
+
return query;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,WAAW,CAAC,IAAY;IAItC,MAAM,UAAU,GAAa,EAAE,CAAC;IAEhC,0DAA0D;IAC1D,wCAAwC;IACxC,MAAM,QAAQ,GAAG,IAAI;SAClB,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC;SACxB,OAAO,CAAC,WAAW,EAAE,CAAC,CAAS,EAAE,IAAY,EAAE,EAAE;QAChD,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtB,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;IAEL,OAAO;QACL,KAAK,EAAE,IAAI,MAAM,CAAC,IAAI,QAAQ,GAAG,CAAC;QAClC,UAAU;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAAc;IACvC,MAAM,KAAK,GAAe,EAAE,CAAC;IAC7B,IAAI,CAAC,MAAM,IAAI,MAAM,KAAK,GAAG;QAAE,OAAO,KAAK,CAAC;IAC5C,MAAM,MAAM,GAAG,IAAI,eAAe,CAChC,MAAM,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAClD,CAAC;IACF,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5B,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACrB,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@praxisjs/router",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"import": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"devDependencies": {
|
|
14
|
+
"typescript": "^5.9.3"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@praxisjs/core": "0.1.0",
|
|
18
|
+
"@praxisjs/jsx": "0.1.0",
|
|
19
|
+
"@praxisjs/shared": "0.1.0"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc",
|
|
23
|
+
"dev": "tsc --watch"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { computed } from "@praxisjs/core";
|
|
2
|
+
import type { Children, VNode } from "@praxisjs/shared";
|
|
3
|
+
|
|
4
|
+
import { useRouter } from "../router";
|
|
5
|
+
|
|
6
|
+
interface LinkProps {
|
|
7
|
+
to: string;
|
|
8
|
+
replace?: boolean;
|
|
9
|
+
class?: string;
|
|
10
|
+
activeClass?: string;
|
|
11
|
+
children?: Children | Children[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function Link({
|
|
15
|
+
to,
|
|
16
|
+
replace = false,
|
|
17
|
+
class: cls = "",
|
|
18
|
+
activeClass = "active",
|
|
19
|
+
children,
|
|
20
|
+
}: LinkProps): VNode {
|
|
21
|
+
const router = useRouter();
|
|
22
|
+
|
|
23
|
+
const isActive = computed(() => router.location().path === to);
|
|
24
|
+
|
|
25
|
+
const handleClick = (e: MouseEvent) => {
|
|
26
|
+
e.preventDefault();
|
|
27
|
+
if (replace) {
|
|
28
|
+
void router.replace(to);
|
|
29
|
+
} else {
|
|
30
|
+
void router.push(to);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<a
|
|
36
|
+
href={to}
|
|
37
|
+
class={() =>
|
|
38
|
+
[cls, isActive() ? activeClass : ""].filter(Boolean).join(" ")
|
|
39
|
+
}
|
|
40
|
+
onClick={handleClick}
|
|
41
|
+
>
|
|
42
|
+
{children}
|
|
43
|
+
</a>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { VNode } from "@praxisjs/shared";
|
|
2
|
+
|
|
3
|
+
import { useRouter } from "../router";
|
|
4
|
+
|
|
5
|
+
export function RouterView(): VNode {
|
|
6
|
+
const router = useRouter();
|
|
7
|
+
|
|
8
|
+
return (
|
|
9
|
+
<div data-router-view="true">
|
|
10
|
+
{() => {
|
|
11
|
+
const Component = router.currentComponent();
|
|
12
|
+
return Component ? <Component /> : null;
|
|
13
|
+
}}
|
|
14
|
+
</div>
|
|
15
|
+
);
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function Route(path: string) {
|
|
2
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
|
+
return function <T extends new (...args: any[]) => any>(constructor: T): T {
|
|
4
|
+
Object.defineProperty(constructor, "__routePath", {
|
|
5
|
+
value: path,
|
|
6
|
+
writable: false,
|
|
7
|
+
configurable: false,
|
|
8
|
+
});
|
|
9
|
+
return constructor;
|
|
10
|
+
};
|
|
11
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export {
|
|
2
|
+
createRouter,
|
|
3
|
+
lazy,
|
|
4
|
+
useRouter,
|
|
5
|
+
useParams,
|
|
6
|
+
useQuery,
|
|
7
|
+
useLocation,
|
|
8
|
+
Router,
|
|
9
|
+
} from "./router";
|
|
10
|
+
export type {
|
|
11
|
+
RouteDefinition,
|
|
12
|
+
RouteLocation,
|
|
13
|
+
RouteParams,
|
|
14
|
+
RouteQuery,
|
|
15
|
+
RouteComponent,
|
|
16
|
+
LazyRouteComponent,
|
|
17
|
+
} from "./types/route";
|
|
18
|
+
|
|
19
|
+
export { RouterView, Link } from "./components";
|
|
20
|
+
export { Route } from "./decorators";
|
package/src/router.ts
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { computed, signal } from "@praxisjs/core";
|
|
2
|
+
import type { Signal, Computed } from "@praxisjs/shared";
|
|
3
|
+
|
|
4
|
+
import { compilePath, parseQuery } from "./utils";
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
CompiledRoute,
|
|
8
|
+
LazyRouteComponent,
|
|
9
|
+
RouteComponent,
|
|
10
|
+
RouteDefinition,
|
|
11
|
+
RouteLocation,
|
|
12
|
+
RouteParams,
|
|
13
|
+
RouteQuery,
|
|
14
|
+
} from "./types/route";
|
|
15
|
+
|
|
16
|
+
export class Router {
|
|
17
|
+
private readonly compiled: CompiledRoute[] = [];
|
|
18
|
+
private readonly _location: Signal<RouteLocation>;
|
|
19
|
+
private _prevLocation: RouteLocation | null = null;
|
|
20
|
+
private readonly _component: Signal<RouteComponent | null>;
|
|
21
|
+
private readonly _loading: Signal<boolean>;
|
|
22
|
+
private readonly _resolvedComponents = new Map<
|
|
23
|
+
LazyRouteComponent,
|
|
24
|
+
RouteComponent
|
|
25
|
+
>();
|
|
26
|
+
|
|
27
|
+
readonly location: Signal<RouteLocation>;
|
|
28
|
+
readonly currentComponent: Signal<RouteComponent | null>;
|
|
29
|
+
readonly loading: Signal<boolean>;
|
|
30
|
+
readonly params: Computed<RouteParams>;
|
|
31
|
+
readonly query: Computed<RouteQuery>;
|
|
32
|
+
|
|
33
|
+
constructor(routes: RouteDefinition[]) {
|
|
34
|
+
for (const route of routes) {
|
|
35
|
+
this.addRoute(route);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const initial = this.buildLocation(
|
|
39
|
+
window.location.pathname,
|
|
40
|
+
window.location.search,
|
|
41
|
+
window.location.hash,
|
|
42
|
+
);
|
|
43
|
+
this._location = signal<RouteLocation>(initial);
|
|
44
|
+
this._loading = signal<boolean>(false);
|
|
45
|
+
this._component = signal<RouteComponent | null>(null);
|
|
46
|
+
|
|
47
|
+
this.location = this._location;
|
|
48
|
+
this.currentComponent = this._component;
|
|
49
|
+
this.loading = this._loading;
|
|
50
|
+
|
|
51
|
+
this.params = computed(() => this._location().params);
|
|
52
|
+
this.query = computed(() => this._location().query);
|
|
53
|
+
|
|
54
|
+
void this.resolveAndSetComponent(initial.path);
|
|
55
|
+
|
|
56
|
+
window.addEventListener("popstate", () => {
|
|
57
|
+
void this.syncFromBrowser();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
private addRoute(route: RouteDefinition, prefix = ""): void {
|
|
62
|
+
const fullPath = prefix + route.path;
|
|
63
|
+
const { regex, paramNames } = compilePath(fullPath);
|
|
64
|
+
this.compiled.push({ definition: route, regex, paramNames });
|
|
65
|
+
|
|
66
|
+
if (route.children) {
|
|
67
|
+
for (const child of route.children) {
|
|
68
|
+
this.addRoute(child, fullPath === "/" ? "" : fullPath);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private buildLocation(
|
|
74
|
+
pathname: string,
|
|
75
|
+
search: string,
|
|
76
|
+
hash: string,
|
|
77
|
+
): RouteLocation {
|
|
78
|
+
const params = this.matchParams(pathname);
|
|
79
|
+
return {
|
|
80
|
+
path: pathname,
|
|
81
|
+
params,
|
|
82
|
+
query: parseQuery(search),
|
|
83
|
+
hash: hash.replace("#", ""),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
private matchParams(path: string): RouteParams {
|
|
88
|
+
for (const route of this.compiled) {
|
|
89
|
+
const match = route.regex.exec(path);
|
|
90
|
+
if (!match) continue;
|
|
91
|
+
const params: RouteParams = {};
|
|
92
|
+
route.paramNames.forEach((name, i) => {
|
|
93
|
+
params[name] = match[i + 1] ?? "";
|
|
94
|
+
});
|
|
95
|
+
return params;
|
|
96
|
+
}
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private isLazy(
|
|
101
|
+
c: RouteComponent | LazyRouteComponent,
|
|
102
|
+
): c is LazyRouteComponent {
|
|
103
|
+
return "__isLazy" in c && (c).__isLazy;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
private async resolveComponent(
|
|
107
|
+
path: string,
|
|
108
|
+
): Promise<RouteComponent | null> {
|
|
109
|
+
for (const route of this.compiled) {
|
|
110
|
+
if (!route.regex.test(path)) continue;
|
|
111
|
+
const { component } = route.definition;
|
|
112
|
+
if (!this.isLazy(component)) return component;
|
|
113
|
+
|
|
114
|
+
const cached = this._resolvedComponents.get(component);
|
|
115
|
+
if (cached) return cached;
|
|
116
|
+
|
|
117
|
+
this._loading.set(true);
|
|
118
|
+
try {
|
|
119
|
+
const mod = await component();
|
|
120
|
+
this._resolvedComponents.set(component, mod.default);
|
|
121
|
+
return mod.default;
|
|
122
|
+
} finally {
|
|
123
|
+
this._loading.set(false);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private async resolveAndSetComponent(path: string): Promise<void> {
|
|
130
|
+
const component = await this.resolveComponent(path);
|
|
131
|
+
this._component.set(component);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private async syncFromBrowser(): Promise<void> {
|
|
135
|
+
const loc = this.buildLocation(
|
|
136
|
+
window.location.pathname,
|
|
137
|
+
window.location.search,
|
|
138
|
+
window.location.hash,
|
|
139
|
+
);
|
|
140
|
+
this._location.set(loc);
|
|
141
|
+
await this.resolveAndSetComponent(loc.path);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async push(path: string, query?: RouteQuery, hash?: string): Promise<void> {
|
|
145
|
+
const search = query ? "?" + new URLSearchParams(query).toString() : "";
|
|
146
|
+
const hashStr = hash ? `#${hash}` : "";
|
|
147
|
+
const fullUrl = path + search + hashStr;
|
|
148
|
+
|
|
149
|
+
const loc = this.buildLocation(path, search, hashStr);
|
|
150
|
+
|
|
151
|
+
const matched = this.compiled.find((r) => r.regex.test(path));
|
|
152
|
+
if (matched?.definition.beforeEnter) {
|
|
153
|
+
const result = await matched.definition.beforeEnter(
|
|
154
|
+
loc,
|
|
155
|
+
this._prevLocation,
|
|
156
|
+
);
|
|
157
|
+
if (result === false) return;
|
|
158
|
+
if (typeof result === "string") {
|
|
159
|
+
return this.push(result);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
this._prevLocation = this._location();
|
|
164
|
+
window.history.pushState(null, "", fullUrl);
|
|
165
|
+
this._location.set(loc);
|
|
166
|
+
await this.resolveAndSetComponent(path);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
async replace(path: string, query?: RouteQuery): Promise<void> {
|
|
170
|
+
const search = query ? "?" + new URLSearchParams(query).toString() : "";
|
|
171
|
+
const loc = this.buildLocation(path, search, "");
|
|
172
|
+
|
|
173
|
+
this._prevLocation = this._location();
|
|
174
|
+
window.history.replaceState(null, "", path + search);
|
|
175
|
+
this._location.set(loc);
|
|
176
|
+
await this.resolveAndSetComponent(path);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
back(): void {
|
|
180
|
+
window.history.back();
|
|
181
|
+
}
|
|
182
|
+
forward(): void {
|
|
183
|
+
window.history.forward();
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
go(delta: number): void {
|
|
187
|
+
window.history.go(delta);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
let _router: Router | null = null;
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
export function lazy(
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
196
|
+
loader: () => Promise<{ default: new (...args: any[]) => any }>,
|
|
197
|
+
): LazyRouteComponent {
|
|
198
|
+
return Object.assign(() => loader(), { __isLazy: true as const });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export function createRouter(routes: RouteDefinition[]): Router {
|
|
202
|
+
_router = new Router(routes);
|
|
203
|
+
return _router;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function useRouter(): Router {
|
|
207
|
+
if (!_router) throw new Error("[Router] createRouter() was not called.");
|
|
208
|
+
return _router;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function useParams(): Computed<RouteParams> {
|
|
212
|
+
return useRouter().params;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function useQuery(): Computed<RouteQuery> {
|
|
216
|
+
return useRouter().query;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function useLocation(): Signal<RouteLocation> {
|
|
220
|
+
return useRouter().location;
|
|
221
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export type RouteParams = Record<string, string>;
|
|
2
|
+
export type RouteQuery = Record<string, string>;
|
|
3
|
+
|
|
4
|
+
export interface RouteLocation {
|
|
5
|
+
path: string;
|
|
6
|
+
params: RouteParams;
|
|
7
|
+
query: RouteQuery;
|
|
8
|
+
hash: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type RouteComponent =
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
|
+
| (new (...args: any[]) => any) // class component
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
|
+
| ((...args: any[]) => any); // function component
|
|
16
|
+
|
|
17
|
+
export interface LazyRouteComponent {
|
|
18
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
19
|
+
(): Promise<{ default: new (...args: any[]) => any }>;
|
|
20
|
+
readonly __isLazy: true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface RouteDefinition {
|
|
24
|
+
path: string;
|
|
25
|
+
component: RouteComponent | LazyRouteComponent;
|
|
26
|
+
children?: RouteDefinition[];
|
|
27
|
+
beforeEnter?: (
|
|
28
|
+
to: RouteLocation,
|
|
29
|
+
from: RouteLocation | null,
|
|
30
|
+
) => boolean | string | Promise<boolean | string>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface CompiledRoute {
|
|
34
|
+
definition: RouteDefinition;
|
|
35
|
+
regex: RegExp;
|
|
36
|
+
paramNames: string[];
|
|
37
|
+
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { RouteQuery } from "./types/route";
|
|
2
|
+
|
|
3
|
+
export function compilePath(path: string): {
|
|
4
|
+
regex: RegExp;
|
|
5
|
+
paramNames: string[];
|
|
6
|
+
} {
|
|
7
|
+
const paramNames: string[] = [];
|
|
8
|
+
|
|
9
|
+
// /users/:id/posts/:postId → /users/([^/]+)/posts/([^/]+)
|
|
10
|
+
// /docs/** → /docs/(.*)
|
|
11
|
+
const regexStr = path
|
|
12
|
+
.replace(/\*\*/g, "(.*)")
|
|
13
|
+
.replace(/:([^/]+)/g, (_: string, name: string) => {
|
|
14
|
+
paramNames.push(name);
|
|
15
|
+
return "([^/]+)";
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
regex: new RegExp(`^${regexStr}$`),
|
|
20
|
+
paramNames,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function parseQuery(search: string): RouteQuery {
|
|
25
|
+
const query: RouteQuery = {};
|
|
26
|
+
if (!search || search === "?") return query;
|
|
27
|
+
const params = new URLSearchParams(
|
|
28
|
+
search.startsWith("?") ? search.slice(1) : search,
|
|
29
|
+
);
|
|
30
|
+
params.forEach((value, key) => {
|
|
31
|
+
query[key] = value;
|
|
32
|
+
});
|
|
33
|
+
return query;
|
|
34
|
+
}
|