@esmx/router 3.0.0-rc.103
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/LICENSE +21 -0
- package/README.md +77 -0
- package/README.zh-CN.md +158 -0
- package/dist/error.d.ts +23 -0
- package/dist/error.mjs +64 -0
- package/dist/increment-id.d.ts +7 -0
- package/dist/increment-id.mjs +16 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.mjs +13 -0
- package/dist/location.d.ts +22 -0
- package/dist/location.mjs +64 -0
- package/dist/matcher.d.ts +4 -0
- package/dist/matcher.mjs +46 -0
- package/dist/micro-app.d.ts +18 -0
- package/dist/micro-app.mjs +85 -0
- package/dist/navigation.d.ts +45 -0
- package/dist/navigation.mjs +153 -0
- package/dist/options.d.ts +4 -0
- package/dist/options.mjs +94 -0
- package/dist/route-task.d.ts +40 -0
- package/dist/route-task.mjs +77 -0
- package/dist/route-transition.d.ts +53 -0
- package/dist/route-transition.mjs +356 -0
- package/dist/route.d.ts +77 -0
- package/dist/route.mjs +223 -0
- package/dist/router-link.d.ts +10 -0
- package/dist/router-link.mjs +139 -0
- package/dist/router.d.ts +122 -0
- package/dist/router.mjs +355 -0
- package/dist/scroll.d.ts +33 -0
- package/dist/scroll.mjs +49 -0
- package/dist/types.d.ts +282 -0
- package/dist/types.mjs +18 -0
- package/dist/util.d.ts +27 -0
- package/dist/util.mjs +67 -0
- package/package.json +62 -0
- package/src/error.ts +84 -0
- package/src/increment-id.ts +12 -0
- package/src/index.ts +67 -0
- package/src/location.ts +124 -0
- package/src/matcher.ts +68 -0
- package/src/micro-app.ts +101 -0
- package/src/navigation.ts +202 -0
- package/src/options.ts +135 -0
- package/src/route-task.ts +102 -0
- package/src/route-transition.ts +472 -0
- package/src/route.ts +335 -0
- package/src/router-link.ts +238 -0
- package/src/router.ts +395 -0
- package/src/scroll.ts +106 -0
- package/src/types.ts +381 -0
- package/src/util.ts +133 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020 Esmx Team
|
|
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.
|
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://esmx.dev/logo.svg?t=2025" width="120" alt="Esmx Logo" />
|
|
3
|
+
<h1>@esmx/router</h1>
|
|
4
|
+
|
|
5
|
+
<div>
|
|
6
|
+
<a href="https://www.npmjs.com/package/@esmx/router">
|
|
7
|
+
<img src="https://img.shields.io/npm/v/@esmx/router.svg" alt="npm version" />
|
|
8
|
+
</a>
|
|
9
|
+
<a href="https://github.com/esmnext/esmx/actions/workflows/build.yml">
|
|
10
|
+
<img src="https://github.com/esmnext/esmx/actions/workflows/build.yml/badge.svg" alt="Build" />
|
|
11
|
+
</a>
|
|
12
|
+
<a href="https://esmx.dev/coverage/">
|
|
13
|
+
<img src="https://img.shields.io/badge/coverage-live%20report-brightgreen" alt="Coverage Report" />
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://nodejs.org/">
|
|
16
|
+
<img src="https://img.shields.io/node/v/@esmx/router.svg" alt="node version" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://bundlephobia.com/package/@esmx/router">
|
|
19
|
+
<img src="https://img.shields.io/bundlephobia/minzip/@esmx/router" alt="size" />
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<p>A universal, framework-agnostic router that works seamlessly with modern frontend frameworks</p>
|
|
24
|
+
|
|
25
|
+
<p>
|
|
26
|
+
English | <a href="https://github.com/esmnext/esmx/blob/master/packages/router/README.zh-CN.md">中文</a>
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## 🚀 Features
|
|
31
|
+
|
|
32
|
+
- **Framework Agnostic** - Works with any frontend framework (Vue, React, Preact, Solid, etc.)
|
|
33
|
+
- **Universal Support** - Runs in both browser and Node.js environments
|
|
34
|
+
- **TypeScript Ready** - Full TypeScript support with excellent type inference
|
|
35
|
+
- **High Performance** - Optimized for production use with minimal bundle size
|
|
36
|
+
- **SSR Compatible** - Complete server-side rendering support
|
|
37
|
+
- **Modern API** - Clean and intuitive API design
|
|
38
|
+
|
|
39
|
+
## 📦 Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# npm
|
|
43
|
+
npm install @esmx/router
|
|
44
|
+
|
|
45
|
+
# pnpm
|
|
46
|
+
pnpm add @esmx/router
|
|
47
|
+
|
|
48
|
+
# yarn
|
|
49
|
+
yarn add @esmx/router
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 🚀 Quick Start
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Router, RouterMode } from '@esmx/router';
|
|
56
|
+
|
|
57
|
+
// Create router instance
|
|
58
|
+
const router = new Router({
|
|
59
|
+
root: '#app', // Required in browser environment
|
|
60
|
+
mode: RouterMode.history,
|
|
61
|
+
routes: [
|
|
62
|
+
{ path: '/', component: () => 'Home Page' },
|
|
63
|
+
{ path: '/about', component: () => 'About Page' }
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Navigate to route
|
|
68
|
+
await router.push('/about');
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 📚 Documentation
|
|
72
|
+
|
|
73
|
+
Visit the [official documentation](https://esmx.dev) for detailed usage guides and API reference.
|
|
74
|
+
|
|
75
|
+
## 📄 License
|
|
76
|
+
|
|
77
|
+
MIT © [Esmx Team](https://github.com/esmnext/esmx)
|
package/README.zh-CN.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
<img src="https://esmx.dev/logo.svg?t=2025" width="120" alt="Esmx Logo" />
|
|
3
|
+
<h1>@esmx/router</h1>
|
|
4
|
+
|
|
5
|
+
<div>
|
|
6
|
+
<a href="https://www.npmjs.com/package/@esmx/router">
|
|
7
|
+
<img src="https://img.shields.io/npm/v/@esmx/router.svg" alt="npm version" />
|
|
8
|
+
</a>
|
|
9
|
+
<a href="https://github.com/esmnext/esmx/actions/workflows/build.yml">
|
|
10
|
+
<img src="https://github.com/esmnext/esmx/actions/workflows/build.yml/badge.svg" alt="Build" />
|
|
11
|
+
</a>
|
|
12
|
+
<a href="https://esmx.dev/coverage/">
|
|
13
|
+
<img src="https://img.shields.io/badge/coverage-live%20report-brightgreen" alt="Coverage Report" />
|
|
14
|
+
</a>
|
|
15
|
+
<a href="https://nodejs.org/">
|
|
16
|
+
<img src="https://img.shields.io/node/v/@esmx/router.svg" alt="node version" />
|
|
17
|
+
</a>
|
|
18
|
+
<a href="https://bundlephobia.com/package/@esmx/router">
|
|
19
|
+
<img src="https://img.shields.io/bundlephobia/minzip/@esmx/router" alt="size" />
|
|
20
|
+
</a>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<p>通用的框架无关路由器,与现代前端框架无缝协作</p>
|
|
24
|
+
|
|
25
|
+
<p>
|
|
26
|
+
<a href="https://github.com/esmnext/esmx/blob/master/packages/router/README.md">English</a> | 中文
|
|
27
|
+
</p>
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
## 🚀 特性
|
|
31
|
+
|
|
32
|
+
- **框架无关** - 适用于任何前端框架(Vue、React、Preact、Solid 等)
|
|
33
|
+
- **通用支持** - 在浏览器和 Node.js 环境中运行
|
|
34
|
+
- **TypeScript 就绪** - 完整的 TypeScript 支持,出色的类型推断
|
|
35
|
+
- **高性能** - 为生产环境优化,最小化包体积
|
|
36
|
+
- **SSR 兼容** - 完整的服务端渲染支持
|
|
37
|
+
- **现代 API** - 简洁直观的 API 设计
|
|
38
|
+
|
|
39
|
+
## 📦 安装
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# npm
|
|
43
|
+
npm install @esmx/router
|
|
44
|
+
|
|
45
|
+
# pnpm
|
|
46
|
+
pnpm add @esmx/router
|
|
47
|
+
|
|
48
|
+
# yarn
|
|
49
|
+
yarn add @esmx/router
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 🚀 快速开始
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { Router, RouterMode } from '@esmx/router';
|
|
56
|
+
|
|
57
|
+
// 创建路由器实例
|
|
58
|
+
const router = new Router({
|
|
59
|
+
root: '#app', // 浏览器环境中必需
|
|
60
|
+
mode: RouterMode.history,
|
|
61
|
+
routes: [
|
|
62
|
+
{ path: '/', component: () => '首页' },
|
|
63
|
+
{ path: '/about', component: () => '关于页面' }
|
|
64
|
+
]
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// 导航到路由
|
|
68
|
+
await router.push('/about');
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 📚 文档
|
|
72
|
+
|
|
73
|
+
访问[官方文档](https://esmx.dev)获取详细的使用指南和 API 参考。
|
|
74
|
+
|
|
75
|
+
### 路由导航时大致的流程说明
|
|
76
|
+
|
|
77
|
+
```mermaid
|
|
78
|
+
flowchart TD
|
|
79
|
+
start(["Start"]):::Terminal --> normalizeURL["normalizeURL"]
|
|
80
|
+
normalizeURL --> isExternalUrl{"是站内地址"}:::Decision
|
|
81
|
+
isExternalUrl -- Yes --> matchInRouteTable["在路由表内匹配"]
|
|
82
|
+
isExternalUrl -- No --> fallback["fallback"] --> End
|
|
83
|
+
matchInRouteTable --> isExist{"存在匹配项"}:::Decision
|
|
84
|
+
isExist -- No --> fallback
|
|
85
|
+
isExist -- Yes --> execGuard["执行其他回调钩子/守卫"] --> End(["End"]):::Terminal
|
|
86
|
+
classDef Terminal fill:#FFF9C4,color:#000
|
|
87
|
+
classDef Decision fill:#C8E6C9,color:#000
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### 路由钩子管道
|
|
91
|
+
|
|
92
|
+
| | fallback | override | beforeLeave | beforeEach | beforeUpdate | beforeEnter | asyncComponent | confirm |
|
|
93
|
+
|---------|----------|----------|-------------|------------|--------------|-------------|----------------|---------|
|
|
94
|
+
| `push` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
95
|
+
| `replace` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
96
|
+
| `pushWindow` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ |
|
|
97
|
+
| `pushLayer` | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ |
|
|
98
|
+
| `replaceWindow` | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ✅ |
|
|
99
|
+
| `restartApp` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
100
|
+
| `unknown` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
|
|
101
|
+
|
|
102
|
+
```mermaid
|
|
103
|
+
gantt
|
|
104
|
+
title 路由钩子执行对比
|
|
105
|
+
dateFormat X
|
|
106
|
+
axisFormat %s
|
|
107
|
+
section push<br>replace
|
|
108
|
+
fallback :0, 1
|
|
109
|
+
override :1, 2
|
|
110
|
+
beforeLeave :2, 3
|
|
111
|
+
beforeEach :3, 4
|
|
112
|
+
beforeUpdate :4, 5
|
|
113
|
+
beforeEnter :5, 6
|
|
114
|
+
asyncComponent:6, 7
|
|
115
|
+
confirm :7, 8
|
|
116
|
+
section pushWindow<br>pushLayer
|
|
117
|
+
fallback :0, 1
|
|
118
|
+
override :1, 2
|
|
119
|
+
beforeEach :3, 4
|
|
120
|
+
confirm :7, 8
|
|
121
|
+
section replaceWindow
|
|
122
|
+
fallback :0, 1
|
|
123
|
+
override :1, 2
|
|
124
|
+
beforeLeave :2, 3
|
|
125
|
+
beforeEach :3, 4
|
|
126
|
+
confirm :7, 8
|
|
127
|
+
section restartApp<br>unknown
|
|
128
|
+
fallback :0, 1
|
|
129
|
+
beforeLeave :2, 3
|
|
130
|
+
beforeEach :3, 4
|
|
131
|
+
beforeUpdate :4, 5
|
|
132
|
+
beforeEnter :5, 6
|
|
133
|
+
asyncComponent:6, 7
|
|
134
|
+
confirm :7, 8
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
#### 钩子函数说明
|
|
138
|
+
|
|
139
|
+
- **fallback**: 处理未匹配的路由
|
|
140
|
+
- **override**: 允许路由重写逻辑
|
|
141
|
+
- **beforeLeave**: 离开当前路由前执行
|
|
142
|
+
- **beforeEach**: 全局导航守卫
|
|
143
|
+
- **beforeUpdate**: 路由更新前执行(相同组件)
|
|
144
|
+
- **beforeEnter**: 进入新路由前执行
|
|
145
|
+
- **asyncComponent**: 加载异步组件
|
|
146
|
+
- **confirm**: 最终确认和导航执行
|
|
147
|
+
|
|
148
|
+
#### 路由类型特点
|
|
149
|
+
|
|
150
|
+
- **标准导航** (`push`、`replace`): 执行完整的钩子链
|
|
151
|
+
- **窗口操作** (`pushWindow`、`replaceWindow`): 简化的钩子链,主要用于窗口级别的导航
|
|
152
|
+
- **层级操作** (`pushLayer`): 最简化的钩子链,用于层级导航
|
|
153
|
+
- **应用重启** (`restartApp`): 完整钩子链但跳过 override
|
|
154
|
+
- **未知类型** (`unknown`): 完整钩子链但跳过 override,作为默认处理
|
|
155
|
+
|
|
156
|
+
## 📄 许可证
|
|
157
|
+
|
|
158
|
+
MIT © [Esmx Team](https://github.com/esmnext/esmx)
|
package/dist/error.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Route } from './route';
|
|
2
|
+
export declare class RouteError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly to: Route;
|
|
5
|
+
readonly from: Route | null;
|
|
6
|
+
constructor(message: string, code: string, to: Route, from?: Route | null);
|
|
7
|
+
}
|
|
8
|
+
export declare class RouteTaskCancelledError extends RouteError {
|
|
9
|
+
readonly taskName: string;
|
|
10
|
+
constructor(taskName: string, to: Route, from?: Route | null);
|
|
11
|
+
}
|
|
12
|
+
export declare class RouteTaskExecutionError extends RouteError {
|
|
13
|
+
readonly taskName: string;
|
|
14
|
+
readonly originalError: Error;
|
|
15
|
+
constructor(taskName: string, to: Route, from?: Route | null, originalError?: unknown);
|
|
16
|
+
}
|
|
17
|
+
export declare class RouteNavigationAbortedError extends RouteError {
|
|
18
|
+
readonly taskName: string;
|
|
19
|
+
constructor(taskName: string, to: Route, from?: Route | null);
|
|
20
|
+
}
|
|
21
|
+
export declare class RouteSelfRedirectionError extends RouteError {
|
|
22
|
+
constructor(fullPath: string, to: Route, from?: Route | null);
|
|
23
|
+
}
|
package/dist/error.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
export class RouteError extends Error {
|
|
5
|
+
constructor(message, code, to, from = null) {
|
|
6
|
+
super(message);
|
|
7
|
+
__publicField(this, "code");
|
|
8
|
+
__publicField(this, "to");
|
|
9
|
+
__publicField(this, "from");
|
|
10
|
+
this.name = "RouteError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.to = to;
|
|
13
|
+
this.from = from;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class RouteTaskCancelledError extends RouteError {
|
|
17
|
+
constructor(taskName, to, from = null) {
|
|
18
|
+
super(
|
|
19
|
+
'Route task "'.concat(taskName, '" was cancelled'),
|
|
20
|
+
"ROUTE_TASK_CANCELLED",
|
|
21
|
+
to,
|
|
22
|
+
from
|
|
23
|
+
);
|
|
24
|
+
__publicField(this, "taskName");
|
|
25
|
+
this.name = "RouteTaskCancelledError";
|
|
26
|
+
this.taskName = taskName;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export class RouteTaskExecutionError extends RouteError {
|
|
30
|
+
constructor(taskName, to, from = null, originalError) {
|
|
31
|
+
const error = originalError instanceof Error ? originalError : new Error(String(originalError));
|
|
32
|
+
const message = 'Route task "'.concat(taskName, '" failed').concat(error.message ? ": ".concat(error.message) : "");
|
|
33
|
+
super(message, "ROUTE_TASK_EXECUTION_ERROR", to, from);
|
|
34
|
+
__publicField(this, "taskName");
|
|
35
|
+
__publicField(this, "originalError");
|
|
36
|
+
this.name = "RouteTaskExecutionError";
|
|
37
|
+
this.taskName = taskName;
|
|
38
|
+
this.originalError = error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export class RouteNavigationAbortedError extends RouteError {
|
|
42
|
+
constructor(taskName, to, from = null) {
|
|
43
|
+
super(
|
|
44
|
+
'Navigation was aborted by task "'.concat(taskName, '"'),
|
|
45
|
+
"ROUTE_NAVIGATION_ABORTED",
|
|
46
|
+
to,
|
|
47
|
+
from
|
|
48
|
+
);
|
|
49
|
+
__publicField(this, "taskName");
|
|
50
|
+
this.name = "RouteNavigationAbortedError";
|
|
51
|
+
this.taskName = taskName;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export class RouteSelfRedirectionError extends RouteError {
|
|
55
|
+
constructor(fullPath, to, from = null) {
|
|
56
|
+
super(
|
|
57
|
+
'Detected a self-redirection to "'.concat(fullPath, '". Aborting navigation.'),
|
|
58
|
+
"ROUTE_SELF_REDIRECTION",
|
|
59
|
+
to,
|
|
60
|
+
from
|
|
61
|
+
);
|
|
62
|
+
this.name = "RouteSelfRedirectionError";
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
export class IncrementId {
|
|
5
|
+
constructor() {
|
|
6
|
+
__publicField(this, "value", 0);
|
|
7
|
+
}
|
|
8
|
+
equal(id) {
|
|
9
|
+
return this.value === id;
|
|
10
|
+
}
|
|
11
|
+
next() {
|
|
12
|
+
return ++this.value;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export const PAGE_ID = new IncrementId();
|
|
16
|
+
export const LAYER_ID = new IncrementId();
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { RouteError, RouteNavigationAbortedError, RouteSelfRedirectionError, RouteTaskCancelledError, RouteTaskExecutionError } from './error';
|
|
2
|
+
export { Route } from './route';
|
|
3
|
+
export { Router } from './router';
|
|
4
|
+
export { type RouteConfig, type RouteConfirmHook, type RouteConfirmHookResult, type RouteHandleHook, type RouteHandleResult, type RouteLayerOptions, type RouteLayerResult, type RouteLocation, type RouteLocationInput, type RouteMatcher, type RouteMatchResult, type RouteMatchType, type RouteMeta, type RouteNotifyHook, type RouteOptions, type RouteParsedConfig, type RouterLayerOptions, type RouterLinkAttributes, type RouterLinkProps, type RouterLinkResolved, type RouterLinkType, type RouterMicroApp, type RouterMicroAppCallback, type RouterMicroAppOptions, RouterMode, type RouterOptions, type RouterParsedOptions, type RouteState, RouteType, type RouteVerifyHook } from './types';
|
|
5
|
+
import type { Router } from './router';
|
|
6
|
+
import type { Route, RouteLocation, RouteLocationInput } from './types';
|
|
7
|
+
/** @deprecated Use `Router` directly instead of `RouterInstance`. */
|
|
8
|
+
export type RouterInstance = Router;
|
|
9
|
+
/** @deprecated Use `RouteLocationInput` directly instead of `RouterRawLocation`. */
|
|
10
|
+
export type RouterRawLocation = RouteLocationInput;
|
|
11
|
+
/** @deprecated Use `RouteLocation` directly instead of `RouterLocation`. */
|
|
12
|
+
export type RouterLocation = RouteLocation;
|
|
13
|
+
/** @deprecated Use `Route` directly instead of `RouteRecord`. */
|
|
14
|
+
export type RouteRecord = Route;
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
RouteError,
|
|
3
|
+
RouteNavigationAbortedError,
|
|
4
|
+
RouteSelfRedirectionError,
|
|
5
|
+
RouteTaskCancelledError,
|
|
6
|
+
RouteTaskExecutionError
|
|
7
|
+
} from "./error.mjs";
|
|
8
|
+
export { Route } from "./route.mjs";
|
|
9
|
+
export { Router } from "./router.mjs";
|
|
10
|
+
export {
|
|
11
|
+
RouterMode,
|
|
12
|
+
RouteType
|
|
13
|
+
} from "./types.mjs";
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { RouteLocation, RouteLocationInput } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Normalizes a URL input into a URL object.
|
|
4
|
+
* @param url - The URL or string to normalize.
|
|
5
|
+
* @param base - The base URL to resolve against if the input is relative.
|
|
6
|
+
* @returns A URL object.
|
|
7
|
+
*/
|
|
8
|
+
export declare function normalizeURL(url: string | URL, base: URL): URL;
|
|
9
|
+
/**
|
|
10
|
+
* Parses a RouteLocationInput object into a full URL.
|
|
11
|
+
* @param toInput - The route location input.
|
|
12
|
+
* @param baseURL - The base URL to resolve against.
|
|
13
|
+
* @returns The parsed URL object.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseLocation(toInput: RouteLocationInput, baseURL: URL): URL;
|
|
16
|
+
/**
|
|
17
|
+
* Resolves RouteLocationInput with fallback from previous route
|
|
18
|
+
* @param toInput - The route location input
|
|
19
|
+
* @param from - The previous route URL (optional)
|
|
20
|
+
* @returns Resolved RouteLocation object
|
|
21
|
+
*/
|
|
22
|
+
export declare function resolveRouteLocationInput(toInput?: RouteLocationInput, from?: URL | null): RouteLocation;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isNotNullish } from "./util.mjs";
|
|
2
|
+
export function normalizeURL(url, base) {
|
|
3
|
+
if (url instanceof URL) {
|
|
4
|
+
return url;
|
|
5
|
+
}
|
|
6
|
+
if (url.startsWith("//")) {
|
|
7
|
+
const protocol = base.protocol;
|
|
8
|
+
return new URL("".concat(protocol).concat(url));
|
|
9
|
+
}
|
|
10
|
+
if (url.startsWith("/")) {
|
|
11
|
+
const newBase = new URL(".", base);
|
|
12
|
+
const parsed = new URL(url, newBase);
|
|
13
|
+
parsed.pathname = newBase.pathname.slice(0, -1) + parsed.pathname;
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
try {
|
|
17
|
+
return new URL(url);
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return new URL(url, base);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function parseLocation(toInput, baseURL) {
|
|
23
|
+
var _a, _b;
|
|
24
|
+
if (typeof toInput === "string") {
|
|
25
|
+
return normalizeURL(toInput, baseURL);
|
|
26
|
+
}
|
|
27
|
+
const url = normalizeURL((_b = (_a = toInput.path) != null ? _a : toInput.url) != null ? _b : "", baseURL);
|
|
28
|
+
const searchParams = url.searchParams;
|
|
29
|
+
const mergedQuery = {};
|
|
30
|
+
if (toInput.query) {
|
|
31
|
+
Object.entries(toInput.query).forEach(([key, value]) => {
|
|
32
|
+
if (typeof value !== "undefined") {
|
|
33
|
+
mergedQuery[key] = value;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (toInput.queryArray) {
|
|
38
|
+
Object.entries(toInput.queryArray).forEach(([key, value]) => {
|
|
39
|
+
if (typeof value !== "undefined") {
|
|
40
|
+
mergedQuery[key] = value;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
Object.entries(mergedQuery).forEach(([key, value]) => {
|
|
45
|
+
searchParams.delete(key);
|
|
46
|
+
value = Array.isArray(value) ? value : [value];
|
|
47
|
+
value.filter((v) => isNotNullish(v) && !Number.isNaN(v)).forEach((v) => {
|
|
48
|
+
searchParams.append(key, String(v));
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
if (toInput.hash) {
|
|
52
|
+
url.hash = toInput.hash;
|
|
53
|
+
}
|
|
54
|
+
return url;
|
|
55
|
+
}
|
|
56
|
+
export function resolveRouteLocationInput(toInput = "/", from = null) {
|
|
57
|
+
if (typeof toInput === "string") {
|
|
58
|
+
return { path: toInput };
|
|
59
|
+
}
|
|
60
|
+
if (toInput && typeof toInput === "object" && typeof toInput.path !== "string" && typeof toInput.url !== "string" && from !== null) {
|
|
61
|
+
return { ...toInput, url: from.href };
|
|
62
|
+
}
|
|
63
|
+
return toInput;
|
|
64
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { RouteConfig, RouteMatcher, RouteParsedConfig } from './types';
|
|
2
|
+
export declare function createMatcher(routes: RouteConfig[], compiledRoutes?: RouteParsedConfig[]): RouteMatcher;
|
|
3
|
+
export declare function createRouteMatches(routes: RouteConfig[], base?: string): RouteParsedConfig[];
|
|
4
|
+
export declare function joinPathname(pathname: string, base?: string): string;
|
package/dist/matcher.mjs
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { compile, match } from "path-to-regexp";
|
|
2
|
+
export function createMatcher(routes, compiledRoutes = createRouteMatches(routes)) {
|
|
3
|
+
return (toURL, baseURL, cb) => {
|
|
4
|
+
const matchPath = toURL.pathname.substring(baseURL.pathname.length - 1);
|
|
5
|
+
const matches = [];
|
|
6
|
+
const params = {};
|
|
7
|
+
const collectMatchingRoutes = (routes2) => {
|
|
8
|
+
for (const item of routes2) {
|
|
9
|
+
if (cb && !cb(item)) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (item.children.length && collectMatchingRoutes(item.children)) {
|
|
13
|
+
matches.unshift(item);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const result = item.match(matchPath);
|
|
17
|
+
if (result) {
|
|
18
|
+
matches.unshift(item);
|
|
19
|
+
if (typeof result === "object") {
|
|
20
|
+
Object.assign(params, result.params);
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
collectMatchingRoutes(compiledRoutes);
|
|
28
|
+
return { matches: Object.freeze(matches), params };
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
export function createRouteMatches(routes, base = "") {
|
|
32
|
+
return routes.map((route) => {
|
|
33
|
+
const compilePath = joinPathname(route.path, base);
|
|
34
|
+
return {
|
|
35
|
+
...route,
|
|
36
|
+
compilePath,
|
|
37
|
+
match: match(compilePath),
|
|
38
|
+
compile: compile(compilePath),
|
|
39
|
+
meta: route.meta || {},
|
|
40
|
+
children: Array.isArray(route.children) ? createRouteMatches(route.children, compilePath) : []
|
|
41
|
+
};
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export function joinPathname(pathname, base = "") {
|
|
45
|
+
return "/" + "".concat(base, "/").concat(pathname).split("/").filter(Boolean).join("/");
|
|
46
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { Router } from './router';
|
|
2
|
+
import type { RouterMicroAppOptions } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves the root container element.
|
|
5
|
+
* Supports a DOM selector string or a direct HTMLElement.
|
|
6
|
+
*
|
|
7
|
+
* @param rootConfig - The root container configuration, can be a selector string or an HTMLElement.
|
|
8
|
+
* @returns The resolved HTMLElement.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resolveRootElement(rootConfig?: string | HTMLElement): HTMLElement;
|
|
11
|
+
export declare class MicroApp {
|
|
12
|
+
app: RouterMicroAppOptions | null;
|
|
13
|
+
root: HTMLElement | null;
|
|
14
|
+
private _factory;
|
|
15
|
+
_update(router: Router, force?: boolean): void;
|
|
16
|
+
private _getNextFactory;
|
|
17
|
+
destroy(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
|
+
import { isBrowser, isPlainObject } from "./util.mjs";
|
|
5
|
+
export function resolveRootElement(rootConfig) {
|
|
6
|
+
let el = null;
|
|
7
|
+
if (rootConfig instanceof HTMLElement) {
|
|
8
|
+
el = rootConfig;
|
|
9
|
+
}
|
|
10
|
+
if (typeof rootConfig === "string" && rootConfig) {
|
|
11
|
+
try {
|
|
12
|
+
el = document.querySelector(rootConfig);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.warn("Failed to resolve root element: ".concat(rootConfig));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
if (el === null) {
|
|
18
|
+
el = document.createElement("div");
|
|
19
|
+
}
|
|
20
|
+
return el;
|
|
21
|
+
}
|
|
22
|
+
export class MicroApp {
|
|
23
|
+
constructor() {
|
|
24
|
+
__publicField(this, "app", null);
|
|
25
|
+
__publicField(this, "root", null);
|
|
26
|
+
__publicField(this, "_factory", null);
|
|
27
|
+
}
|
|
28
|
+
_update(router, force = false) {
|
|
29
|
+
const factory = this._getNextFactory(router);
|
|
30
|
+
if (!force && factory === this._factory) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const oldApp = this.app;
|
|
34
|
+
const app = factory ? factory(router) : null;
|
|
35
|
+
if (isBrowser && app) {
|
|
36
|
+
let root = this.root;
|
|
37
|
+
if (root === null) {
|
|
38
|
+
root = resolveRootElement(router.root);
|
|
39
|
+
const { rootStyle } = router.parsedOptions;
|
|
40
|
+
if (root && isPlainObject(rootStyle)) {
|
|
41
|
+
Object.assign(root.style, router.parsedOptions.rootStyle);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (root) {
|
|
45
|
+
app.mount(root);
|
|
46
|
+
if (root.parentNode === null) {
|
|
47
|
+
document.body.appendChild(root);
|
|
48
|
+
}
|
|
49
|
+
this.root = root;
|
|
50
|
+
}
|
|
51
|
+
if (oldApp) {
|
|
52
|
+
oldApp.unmount();
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
this.app = app;
|
|
56
|
+
this._factory = factory;
|
|
57
|
+
}
|
|
58
|
+
_getNextFactory({
|
|
59
|
+
route,
|
|
60
|
+
options
|
|
61
|
+
}) {
|
|
62
|
+
if (!route.matched || route.matched.length === 0) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const name = route.matched[0].app;
|
|
66
|
+
if (typeof name === "string" && options.apps && typeof options.apps === "object") {
|
|
67
|
+
return options.apps[name] || null;
|
|
68
|
+
}
|
|
69
|
+
if (typeof name === "function") {
|
|
70
|
+
return name;
|
|
71
|
+
}
|
|
72
|
+
if (typeof options.apps === "function") {
|
|
73
|
+
return options.apps;
|
|
74
|
+
}
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
destroy() {
|
|
78
|
+
var _a, _b;
|
|
79
|
+
(_a = this.app) == null ? void 0 : _a.unmount();
|
|
80
|
+
this.app = null;
|
|
81
|
+
(_b = this.root) == null ? void 0 : _b.remove();
|
|
82
|
+
this.root = null;
|
|
83
|
+
this._factory = null;
|
|
84
|
+
}
|
|
85
|
+
}
|