@iyulab/router 0.5.1 → 0.5.3
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 +20 -20
- package/README.md +162 -162
- package/dist/index.d.ts +177 -119
- package/dist/index.js +161 -102
- package/package.json +50 -50
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 iyulab
|
|
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
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 iyulab
|
|
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
21
|
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,163 +1,163 @@
|
|
|
1
|
-
# @iyulab/router
|
|
2
|
-
|
|
3
|
-
A modern, lightweight client-side router for web applications with support for both Lit and React components.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🚀 **Modern URLPattern-based routing** - Uses native URLPattern API for powerful path matching
|
|
8
|
-
- 🔧 **Unified Framework Support** - Works with both Lit and React components using render functions
|
|
9
|
-
- 📱 **Client-side Navigation** - History API integration with browser back/forward support
|
|
10
|
-
- 🎯 **Nested Routing** - Support for deeply nested route hierarchies with index and path routes
|
|
11
|
-
- 🔗 **Smart Link Component** - Automatic external link detection and handling
|
|
12
|
-
- 📊 **Route Events** - Track navigation progress with route-begin, route-done, and route-error events
|
|
13
|
-
- 🎨 **Flexible Outlet System** - Unified rendering with renderContent method
|
|
14
|
-
- 🌐 **Global Route Access** - Access current route information anywhere via `window.route`
|
|
15
|
-
- 🔄 **Force Re-rendering** - Control component re-rendering on route changes
|
|
16
|
-
- ⚠️ **Enhanced Error Handling** - Built-in ErrorPage component with improved styling
|
|
17
|
-
|
|
18
|
-
## Installation
|
|
19
|
-
|
|
20
|
-
```bash
|
|
21
|
-
npm install @iyulab/router
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
## Quick Start
|
|
25
|
-
|
|
26
|
-
### Basic Setup
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
import { Router } from '@iyulab/router';
|
|
30
|
-
import { html } from 'lit';
|
|
31
|
-
|
|
32
|
-
const router = new Router({
|
|
33
|
-
root: document.getElementById('app')!,
|
|
34
|
-
basepath: '/app',
|
|
35
|
-
routes: [
|
|
36
|
-
{
|
|
37
|
-
index: true,
|
|
38
|
-
render: () => html`<home-page></home-page>`
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
path: '/user/:id',
|
|
42
|
-
render: (routeInfo) => html`<user-page .userId=${routeInfo.params.id}></user-page>`
|
|
43
|
-
}
|
|
44
|
-
]
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Start routing
|
|
48
|
-
router.go(window.location.href);
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Nested Routes
|
|
52
|
-
|
|
53
|
-
```typescript
|
|
54
|
-
const routes = [
|
|
55
|
-
{
|
|
56
|
-
path: '/dashboard',
|
|
57
|
-
render: () => html`<dashboard-layout><u-outlet></u-outlet></dashboard-layout>`,
|
|
58
|
-
children: [
|
|
59
|
-
{
|
|
60
|
-
index: true, // Matches /dashboard exactly
|
|
61
|
-
render: () => html`<dashboard-home></dashboard-home>`
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
path: 'settings',
|
|
65
|
-
render: () => html`<dashboard-settings></dashboard-settings>`
|
|
66
|
-
}
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
];
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Mixed Framework Support
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
import React from 'react';
|
|
76
|
-
|
|
77
|
-
const routes = [
|
|
78
|
-
// Lit component
|
|
79
|
-
{
|
|
80
|
-
path: '/lit-page',
|
|
81
|
-
render: (routeInfo) => html`<my-lit-component .routeInfo=${routeInfo}></my-lit-component>`
|
|
82
|
-
},
|
|
83
|
-
// React component
|
|
84
|
-
{
|
|
85
|
-
path: '/react-page',
|
|
86
|
-
render: (routeInfo) => React.createElement(MyReactComponent, { routeInfo })
|
|
87
|
-
},
|
|
88
|
-
// HTML element
|
|
89
|
-
{
|
|
90
|
-
path: '/element-page',
|
|
91
|
-
render: (routeInfo) => {
|
|
92
|
-
const element = document.createElement('my-element');
|
|
93
|
-
element.data = routeInfo.params;
|
|
94
|
-
return element;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
];
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Usage Examples
|
|
101
|
-
|
|
102
|
-
### Using with Lit Components
|
|
103
|
-
|
|
104
|
-
```typescript
|
|
105
|
-
import { LitElement, html } from 'lit';
|
|
106
|
-
import { customElement } from 'lit/decorators.js';
|
|
107
|
-
|
|
108
|
-
@customElement('app-root')
|
|
109
|
-
export class AppRoot extends LitElement {
|
|
110
|
-
render() {
|
|
111
|
-
return html`
|
|
112
|
-
<nav>
|
|
113
|
-
<u-link href="/">Home</u-link>
|
|
114
|
-
<u-link href="/about">About</u-link>
|
|
115
|
-
<u-link href="/user/123">User Profile</u-link>
|
|
116
|
-
</nav>
|
|
117
|
-
<main>
|
|
118
|
-
<u-outlet></u-outlet>
|
|
119
|
-
</main>
|
|
120
|
-
`;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
### Using with React Components
|
|
126
|
-
|
|
127
|
-
```tsx
|
|
128
|
-
import React from 'react';
|
|
129
|
-
import { Outlet, Link } from '@iyulab/router';
|
|
130
|
-
|
|
131
|
-
export function AppRoot() {
|
|
132
|
-
return (
|
|
133
|
-
<div>
|
|
134
|
-
<nav>
|
|
135
|
-
<Link href="/">Home</Link>
|
|
136
|
-
<Link href="/about">About</Link>
|
|
137
|
-
<Link href="/user/123">User Profile</Link>
|
|
138
|
-
</nav>
|
|
139
|
-
<main>
|
|
140
|
-
<Outlet />
|
|
141
|
-
</main>
|
|
142
|
-
</div>
|
|
143
|
-
);
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## Browser Support
|
|
148
|
-
|
|
149
|
-
- **URLPattern API**: Required for routing functionality
|
|
150
|
-
- **Modern browsers**: Chrome 95+, Firefox 106+, Safari 16.4+
|
|
151
|
-
- **Polyfill**: Consider using [urlpattern-polyfill](https://www.npmjs.com/package/urlpattern-polyfill) for older browsers
|
|
152
|
-
|
|
153
|
-
## Contributing
|
|
154
|
-
|
|
155
|
-
1. Fork the repository
|
|
156
|
-
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
157
|
-
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
158
|
-
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
159
|
-
5. Open a Pull Request
|
|
160
|
-
|
|
161
|
-
## License
|
|
162
|
-
|
|
1
|
+
# @iyulab/router
|
|
2
|
+
|
|
3
|
+
A modern, lightweight client-side router for web applications with support for both Lit and React components.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Modern URLPattern-based routing** - Uses native URLPattern API for powerful path matching
|
|
8
|
+
- 🔧 **Unified Framework Support** - Works with both Lit and React components using render functions
|
|
9
|
+
- 📱 **Client-side Navigation** - History API integration with browser back/forward support
|
|
10
|
+
- 🎯 **Nested Routing** - Support for deeply nested route hierarchies with index and path routes
|
|
11
|
+
- 🔗 **Smart Link Component** - Automatic external link detection and handling
|
|
12
|
+
- 📊 **Route Events** - Track navigation progress with route-begin, route-done, and route-error events
|
|
13
|
+
- 🎨 **Flexible Outlet System** - Unified rendering with renderContent method
|
|
14
|
+
- 🌐 **Global Route Access** - Access current route information anywhere via `window.route`
|
|
15
|
+
- 🔄 **Force Re-rendering** - Control component re-rendering on route changes
|
|
16
|
+
- ⚠️ **Enhanced Error Handling** - Built-in ErrorPage component with improved styling
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @iyulab/router
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
### Basic Setup
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Router } from '@iyulab/router';
|
|
30
|
+
import { html } from 'lit';
|
|
31
|
+
|
|
32
|
+
const router = new Router({
|
|
33
|
+
root: document.getElementById('app')!,
|
|
34
|
+
basepath: '/app',
|
|
35
|
+
routes: [
|
|
36
|
+
{
|
|
37
|
+
index: true,
|
|
38
|
+
render: () => html`<home-page></home-page>`
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
path: '/user/:id',
|
|
42
|
+
render: (routeInfo) => html`<user-page .userId=${routeInfo.params.id}></user-page>`
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Start routing
|
|
48
|
+
router.go(window.location.href);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Nested Routes
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
const routes = [
|
|
55
|
+
{
|
|
56
|
+
path: '/dashboard',
|
|
57
|
+
render: () => html`<dashboard-layout><u-outlet></u-outlet></dashboard-layout>`,
|
|
58
|
+
children: [
|
|
59
|
+
{
|
|
60
|
+
index: true, // Matches /dashboard exactly
|
|
61
|
+
render: () => html`<dashboard-home></dashboard-home>`
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
path: 'settings',
|
|
65
|
+
render: () => html`<dashboard-settings></dashboard-settings>`
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
];
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Mixed Framework Support
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
import React from 'react';
|
|
76
|
+
|
|
77
|
+
const routes = [
|
|
78
|
+
// Lit component
|
|
79
|
+
{
|
|
80
|
+
path: '/lit-page',
|
|
81
|
+
render: (routeInfo) => html`<my-lit-component .routeInfo=${routeInfo}></my-lit-component>`
|
|
82
|
+
},
|
|
83
|
+
// React component
|
|
84
|
+
{
|
|
85
|
+
path: '/react-page',
|
|
86
|
+
render: (routeInfo) => React.createElement(MyReactComponent, { routeInfo })
|
|
87
|
+
},
|
|
88
|
+
// HTML element
|
|
89
|
+
{
|
|
90
|
+
path: '/element-page',
|
|
91
|
+
render: (routeInfo) => {
|
|
92
|
+
const element = document.createElement('my-element');
|
|
93
|
+
element.data = routeInfo.params;
|
|
94
|
+
return element;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
];
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Usage Examples
|
|
101
|
+
|
|
102
|
+
### Using with Lit Components
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { LitElement, html } from 'lit';
|
|
106
|
+
import { customElement } from 'lit/decorators.js';
|
|
107
|
+
|
|
108
|
+
@customElement('app-root')
|
|
109
|
+
export class AppRoot extends LitElement {
|
|
110
|
+
render() {
|
|
111
|
+
return html`
|
|
112
|
+
<nav>
|
|
113
|
+
<u-link href="/">Home</u-link>
|
|
114
|
+
<u-link href="/about">About</u-link>
|
|
115
|
+
<u-link href="/user/123">User Profile</u-link>
|
|
116
|
+
</nav>
|
|
117
|
+
<main>
|
|
118
|
+
<u-outlet></u-outlet>
|
|
119
|
+
</main>
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Using with React Components
|
|
126
|
+
|
|
127
|
+
```tsx
|
|
128
|
+
import React from 'react';
|
|
129
|
+
import { Outlet, Link } from '@iyulab/router';
|
|
130
|
+
|
|
131
|
+
export function AppRoot() {
|
|
132
|
+
return (
|
|
133
|
+
<div>
|
|
134
|
+
<nav>
|
|
135
|
+
<Link href="/">Home</Link>
|
|
136
|
+
<Link href="/about">About</Link>
|
|
137
|
+
<Link href="/user/123">User Profile</Link>
|
|
138
|
+
</nav>
|
|
139
|
+
<main>
|
|
140
|
+
<Outlet />
|
|
141
|
+
</main>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Browser Support
|
|
148
|
+
|
|
149
|
+
- **URLPattern API**: Required for routing functionality
|
|
150
|
+
- **Modern browsers**: Chrome 95+, Firefox 106+, Safari 16.4+
|
|
151
|
+
- **Polyfill**: Consider using [urlpattern-polyfill](https://www.npmjs.com/package/urlpattern-polyfill) for older browsers
|
|
152
|
+
|
|
153
|
+
## Contributing
|
|
154
|
+
|
|
155
|
+
1. Fork the repository
|
|
156
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
157
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
158
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
159
|
+
5. Open a Pull Request
|
|
160
|
+
|
|
161
|
+
## License
|
|
162
|
+
|
|
163
163
|
MIT License - see [LICENSE](LICENSE) file for details.
|
package/dist/index.d.ts
CHANGED
|
@@ -18,28 +18,48 @@ declare interface BaseRouteConfig {
|
|
|
18
18
|
*/
|
|
19
19
|
title?: string;
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
* @
|
|
21
|
+
* 라우터 경로는 string 또는 URLPattern을 사용할 수 있습니다.
|
|
22
|
+
* string일 경우 자동으로 URLPattern으로 변환됩니다.
|
|
23
|
+
* @default '/'
|
|
24
|
+
* @example
|
|
25
|
+
* - "/user/:id/:name"
|
|
26
|
+
* - "/user/:id/:name?"
|
|
27
|
+
* - "/user/:id/:name*"
|
|
28
|
+
* - "/user/:id/:name+"
|
|
29
|
+
* @link
|
|
30
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
|
|
31
|
+
*/
|
|
32
|
+
path?: string | URLPattern;
|
|
33
|
+
/**
|
|
34
|
+
* 라우트 정보를 받아 렌더링 결과를 반환합니다.
|
|
35
|
+
* @param ctx 현재 라우팅 정보 및, 진행 상태 콜백을 포함하는 Context 객체가 인자로 전달됩니다.
|
|
24
36
|
* @example
|
|
25
37
|
* ```typescript
|
|
26
38
|
* const route = {
|
|
27
|
-
* path: '/user',
|
|
28
|
-
* render: (
|
|
39
|
+
* path: '/user:id',
|
|
40
|
+
* render: async (ctx) => {
|
|
41
|
+
* // 사용자 정보를 비동기로 가져오는 예시
|
|
42
|
+
* const userId = ctx.params.id;
|
|
43
|
+
* ctx.progress(30);
|
|
44
|
+
* const userData = await fetchUserData(userId);
|
|
45
|
+
* ctx.progress(70);
|
|
46
|
+
* return html`<user-profile .data=${userData}></user-profile>`;
|
|
47
|
+
* }
|
|
29
48
|
* }
|
|
30
49
|
* ```
|
|
31
50
|
*/
|
|
32
|
-
render?: (
|
|
33
|
-
/**
|
|
34
|
-
* 중첩 라우트
|
|
35
|
-
*/
|
|
36
|
-
children?: RouteConfig[];
|
|
51
|
+
render?: (ctx: RouteContext) => Promise<RenderResult> | RenderResult;
|
|
37
52
|
/**
|
|
38
53
|
* 라우터 URL 변경시 렌더링을 강제할지 여부
|
|
39
54
|
* - 기본값으로 children을 가질때 false로 설정되며, children이 없을 경우 true로 설정됩니다.
|
|
40
55
|
* - true로 설정하면 기존 렌더링을 무시하고 새로 렌더링합니다.
|
|
41
56
|
*/
|
|
42
57
|
force?: boolean;
|
|
58
|
+
/**
|
|
59
|
+
* 경로 매칭시 대소문자 구분 여부
|
|
60
|
+
* @default false
|
|
61
|
+
*/
|
|
62
|
+
ignoreCase?: boolean;
|
|
43
63
|
}
|
|
44
64
|
|
|
45
65
|
/**
|
|
@@ -56,20 +76,47 @@ export declare class ContentRenderError extends RouteError {
|
|
|
56
76
|
constructor(original?: Error | any);
|
|
57
77
|
}
|
|
58
78
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
declare interface IndexRouteConfig extends BaseRouteConfig {
|
|
79
|
+
export declare type FallbackRenderResult = HTMLElement | ReactElement | TemplateResult<1>;
|
|
80
|
+
|
|
81
|
+
export declare interface FallbackRouteConfig {
|
|
63
82
|
/**
|
|
64
|
-
*
|
|
65
|
-
* path는 강제로 빈 문자열로 설정됩니다.
|
|
83
|
+
* 브라우저의 타이틀이 설정에 따라 변경됩니다.
|
|
66
84
|
*/
|
|
67
|
-
|
|
85
|
+
title?: string;
|
|
68
86
|
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
87
|
+
* 라우팅 실패 시 표시할 렌더링 결과를 반환합니다.
|
|
88
|
+
* - 오류가 발생할 경우 또는 렌더링 결과가 false일 경우 호출됩니다.
|
|
89
|
+
* @param ctx 현재 라우팅 정보 및 오류 정보를 포함하는 Context 객체가 인자로 전달됩니다.
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* const fallbackRoute = {
|
|
93
|
+
* title: 'Not Found',
|
|
94
|
+
* render: (ctx) => {
|
|
95
|
+
* if (ctx.error) {
|
|
96
|
+
* return html`<error-page .error=${ctx.error}></error-page>`;
|
|
97
|
+
* }
|
|
98
|
+
* return html`<not-found-page></not-found-page>`;
|
|
99
|
+
* }
|
|
100
|
+
* }
|
|
101
|
+
* ```
|
|
71
102
|
*/
|
|
72
|
-
|
|
103
|
+
render?: (ctx: FallbackRouteContext) => Promise<FallbackRenderResult> | FallbackRenderResult;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export declare interface FallbackRouteContext extends RouteContext {
|
|
107
|
+
/**
|
|
108
|
+
* 라우팅 에러 정보
|
|
109
|
+
* - 라우팅 중 발생한 에러 정보를 포함합니다.
|
|
110
|
+
*/
|
|
111
|
+
error: RouteError;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
declare interface IndexRouteConfig extends BaseRouteConfig {
|
|
115
|
+
/**
|
|
116
|
+
* 현재 경로의 인덱스 라우트임을 나타냅니다.
|
|
117
|
+
* - 인덱스 라우트는 부모 경로와 동일한 경로를 가지며, path는 자동으로 설정됩니다.
|
|
118
|
+
*/
|
|
119
|
+
index: true;
|
|
73
120
|
}
|
|
74
121
|
|
|
75
122
|
/**
|
|
@@ -113,6 +160,18 @@ export declare class Link extends LitElement {
|
|
|
113
160
|
static styles: CSSResult;
|
|
114
161
|
}
|
|
115
162
|
|
|
163
|
+
declare interface NonIndexRouteConfig extends BaseRouteConfig {
|
|
164
|
+
/**
|
|
165
|
+
* 인덱스 라우트가 아님을 나타냅니다.
|
|
166
|
+
*/
|
|
167
|
+
index?: false;
|
|
168
|
+
/**
|
|
169
|
+
* 하위 라우트 설정, 재귀적으로 RouteConfig 배열을 가질 수 있습니다.
|
|
170
|
+
* - 하위 라우트가 있는 경우, 부모 라우트의 경로를 기준으로 매칭됩니다.
|
|
171
|
+
*/
|
|
172
|
+
children?: RouteConfig[];
|
|
173
|
+
}
|
|
174
|
+
|
|
116
175
|
/**
|
|
117
176
|
* 페이지를 찾을 수 없을 때 발생하는 에러
|
|
118
177
|
*/
|
|
@@ -149,101 +208,27 @@ export declare class OutletMissingError extends RouteError {
|
|
|
149
208
|
constructor();
|
|
150
209
|
}
|
|
151
210
|
|
|
152
|
-
/**
|
|
153
|
-
* 경로 라우트 타입
|
|
154
|
-
*/
|
|
155
|
-
declare interface PathRouteConfig extends BaseRouteConfig {
|
|
156
|
-
/**
|
|
157
|
-
* 라우터 경로는 string 또는 URLPattern을 사용할 수 있습니다.
|
|
158
|
-
* string일 경우 자동으로 URLPattern으로 변환됩니다.
|
|
159
|
-
* @example
|
|
160
|
-
* - "/user/:id/:name"
|
|
161
|
-
* - "/user/:id/:name?"
|
|
162
|
-
* - "/user/:id/:name*"
|
|
163
|
-
* - "/user/:id/:name+"
|
|
164
|
-
* - "/user/:id/:name{1,3}"
|
|
165
|
-
* @link
|
|
166
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
|
|
167
|
-
*/
|
|
168
|
-
path: string | URLPattern;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
211
|
declare interface RenderOption {
|
|
172
212
|
id?: string;
|
|
173
213
|
force?: boolean;
|
|
174
214
|
content: RenderResult;
|
|
175
215
|
}
|
|
176
216
|
|
|
177
|
-
declare type RenderResult = HTMLElement | ReactElement | TemplateResult<1
|
|
217
|
+
export declare type RenderResult = HTMLElement | ReactElement | TemplateResult<1> | false;
|
|
178
218
|
|
|
179
219
|
/**
|
|
180
220
|
* 라우트 시작 이벤트
|
|
181
221
|
*/
|
|
182
222
|
export declare class RouteBeginEvent extends RouteEvent {
|
|
183
|
-
constructor(
|
|
223
|
+
constructor(context: RouteContext);
|
|
184
224
|
}
|
|
185
225
|
|
|
186
|
-
|
|
187
|
-
* 라우트 타입 (인덱스 라우트 또는 경로 라우트)
|
|
188
|
-
*/
|
|
189
|
-
export declare type RouteConfig = IndexRouteConfig | PathRouteConfig;
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* 라우트 완료 이벤트
|
|
193
|
-
*/
|
|
194
|
-
export declare class RouteDoneEvent extends RouteEvent {
|
|
195
|
-
constructor(routeInfo: RouteInfo);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* 라우팅 에러 정보
|
|
200
|
-
*/
|
|
201
|
-
export declare class RouteError extends Error {
|
|
202
|
-
/**
|
|
203
|
-
* 에러 코드
|
|
204
|
-
* - HTTP 상태 코드 또는 커스텀 에러 코드
|
|
205
|
-
* @example 404, 500, 'ROUTE_NOT_FOUND'
|
|
206
|
-
*/
|
|
207
|
-
code: number | string;
|
|
208
|
-
/**
|
|
209
|
-
* 원본 에러 객체
|
|
210
|
-
* - 원본 Error 객체 또는 예외 정보
|
|
211
|
-
*/
|
|
212
|
-
original?: Error | any;
|
|
213
|
-
/**
|
|
214
|
-
* 에러 발생 시간
|
|
215
|
-
* - 에러가 발생한 시간 (ISO 8601 형식)
|
|
216
|
-
*/
|
|
217
|
-
timestamp: string;
|
|
218
|
-
constructor(code: number | string, message: string, original?: Error | any);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* 라우트 에러 이벤트
|
|
223
|
-
*/
|
|
224
|
-
export declare class RouteErrorEvent extends RouteEvent {
|
|
225
|
-
/** 에러 정보 */
|
|
226
|
-
readonly error: RouteError;
|
|
227
|
-
constructor(error: RouteError, routeInfo: RouteInfo);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/** 라우터 이벤트 기본 클래스 */
|
|
231
|
-
declare abstract class RouteEvent extends Event {
|
|
232
|
-
/** 라우팅 정보 */
|
|
233
|
-
readonly routeInfo: RouteInfo;
|
|
234
|
-
/** 이벤트 발생 시간 */
|
|
235
|
-
readonly timestamp: string;
|
|
236
|
-
constructor(type: string, routeInfo: RouteInfo, cancelable?: boolean);
|
|
237
|
-
/** 이벤트가 취소되었는지 확인 */
|
|
238
|
-
get cancelled(): boolean;
|
|
239
|
-
/** 이벤트 취소 */
|
|
240
|
-
cancel(): void;
|
|
241
|
-
}
|
|
226
|
+
export declare type RouteConfig = IndexRouteConfig | NonIndexRouteConfig;
|
|
242
227
|
|
|
243
228
|
/**
|
|
244
229
|
* 라우터 정보
|
|
245
230
|
*/
|
|
246
|
-
export declare interface
|
|
231
|
+
export declare interface RouteContext {
|
|
247
232
|
/**
|
|
248
233
|
* 전체 URL 정보
|
|
249
234
|
* - 도메인 이름을 포함한 URL의 전체 경로입니다.
|
|
@@ -305,6 +290,73 @@ export declare interface RouteInfo {
|
|
|
305
290
|
* @example #profile
|
|
306
291
|
*/
|
|
307
292
|
hash?: string;
|
|
293
|
+
/**
|
|
294
|
+
* 현재 라우팅의 진행 상태를 업데이트하여, window 객체의 'route-progress' 이벤트를 트리거합니다.
|
|
295
|
+
* 여러 라우팅이 호출되는 경우, 가장 최근의 라우팅의 진행 상태만 반영되며, 나머지는 무시됩니다.
|
|
296
|
+
* @param value 진행 상태 값 (0~100)
|
|
297
|
+
*/
|
|
298
|
+
progress: (value: number) => void;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* 라우트 완료 이벤트
|
|
303
|
+
*/
|
|
304
|
+
export declare class RouteDoneEvent extends RouteEvent {
|
|
305
|
+
constructor(context: RouteContext);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* 라우팅 에러 정보
|
|
310
|
+
*/
|
|
311
|
+
export declare class RouteError extends Error {
|
|
312
|
+
/**
|
|
313
|
+
* 에러 코드
|
|
314
|
+
* - HTTP 상태 코드 또는 커스텀 에러 코드
|
|
315
|
+
* @example 404, 500, 'ROUTE_NOT_FOUND'
|
|
316
|
+
*/
|
|
317
|
+
code: number | string;
|
|
318
|
+
/**
|
|
319
|
+
* 원본 에러 객체
|
|
320
|
+
* - 원본 Error 객체 또는 예외 정보
|
|
321
|
+
*/
|
|
322
|
+
original?: Error | any;
|
|
323
|
+
/**
|
|
324
|
+
* 에러 발생 시간
|
|
325
|
+
* - 에러가 발생한 시간 (ISO 8601 형식)
|
|
326
|
+
*/
|
|
327
|
+
timestamp: string;
|
|
328
|
+
constructor(code: number | string, message: string, original?: Error | any);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* 라우트 에러 이벤트
|
|
333
|
+
*/
|
|
334
|
+
export declare class RouteErrorEvent extends RouteEvent {
|
|
335
|
+
/** 에러 정보 */
|
|
336
|
+
readonly error: RouteError;
|
|
337
|
+
constructor(context: RouteContext, error: RouteError);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** 라우터 이벤트 기본 클래스 */
|
|
341
|
+
declare abstract class RouteEvent extends Event {
|
|
342
|
+
/** 라우팅 정보 */
|
|
343
|
+
readonly context: RouteContext;
|
|
344
|
+
/** 이벤트 발생 시간 */
|
|
345
|
+
readonly timestamp: string;
|
|
346
|
+
constructor(type: string, context: RouteContext, cancelable?: boolean);
|
|
347
|
+
/** 이벤트가 취소되었는지 확인 */
|
|
348
|
+
get cancelled(): boolean;
|
|
349
|
+
/** 이벤트 취소 */
|
|
350
|
+
cancel(): void;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 라우트 진행 이벤트
|
|
355
|
+
*/
|
|
356
|
+
export declare class RouteProgressEvent extends RouteEvent {
|
|
357
|
+
/** 진행 상태 값 (0~100) */
|
|
358
|
+
readonly progress: number;
|
|
359
|
+
constructor(context: RouteContext, progress: number);
|
|
308
360
|
}
|
|
309
361
|
|
|
310
362
|
/**
|
|
@@ -314,21 +366,20 @@ export declare class Router {
|
|
|
314
366
|
private readonly _rootElement;
|
|
315
367
|
private readonly _basepath;
|
|
316
368
|
private readonly _routes;
|
|
369
|
+
private readonly _fallback?;
|
|
317
370
|
/** 현재 라우팅 요청 ID */
|
|
318
371
|
private _requestID?;
|
|
319
372
|
/** 현재 라우팅 정보 */
|
|
320
|
-
private
|
|
373
|
+
private _context?;
|
|
321
374
|
constructor(config: RouterConfig);
|
|
322
|
-
/** 초기 라우팅 처리, TODO: 제거 */
|
|
323
|
-
private waitConnected;
|
|
324
375
|
/** 객체를 정리하고 이벤트 리스너를 제거합니다. */
|
|
325
376
|
destroy(): void;
|
|
326
377
|
/** 라우터의 기본 경로 반환 */
|
|
327
378
|
get basepath(): string;
|
|
328
|
-
/** 등록된 라우트 반환 */
|
|
379
|
+
/** 등록된 라우트 정보 반환 */
|
|
329
380
|
get routes(): RouteConfig[];
|
|
330
381
|
/** 현재 라우팅 정보 반환 */
|
|
331
|
-
get
|
|
382
|
+
get context(): RouteContext | undefined;
|
|
332
383
|
/**
|
|
333
384
|
* 지정한 경로의 클라이언트 라우팅을 수행합니다. 상대경로일 경우 basepath와 조합되어 이동합니다.
|
|
334
385
|
* @param href 이동할 경로
|
|
@@ -360,12 +411,22 @@ export declare interface RouterConfig {
|
|
|
360
411
|
* - 라우트는 URLPattern을 사용하여 경로를 탐색합니다.
|
|
361
412
|
* - 라우트는 렌더링할 엘리먼트 또는 컴포넌트를 지정합니다.
|
|
362
413
|
*/
|
|
363
|
-
routes
|
|
414
|
+
routes?: RouteConfig[];
|
|
415
|
+
/**
|
|
416
|
+
* 라우트 매칭 실패 또는 오류 발생 시 대체 라우트 설정
|
|
417
|
+
* - 지정된 설정이 없을 경우, 기본 오류 페이지가 렌더링됩니다.
|
|
418
|
+
*/
|
|
419
|
+
fallback?: FallbackRouteConfig;
|
|
364
420
|
/**
|
|
365
421
|
* `a` 태그 클릭 시 클라이언트 라우팅을 수행할지 여부를 설정합니다.
|
|
366
422
|
* @default true
|
|
367
423
|
*/
|
|
368
424
|
useIntercept?: boolean;
|
|
425
|
+
/**
|
|
426
|
+
* 초기 로드 시 현재 URL로 라우팅을 자동으로 수행할지 여부를 설정합니다.
|
|
427
|
+
* @default true
|
|
428
|
+
*/
|
|
429
|
+
initialLoad?: boolean;
|
|
369
430
|
}
|
|
370
431
|
|
|
371
432
|
export declare const ULink: ReactWebComponent<Link, {}>;
|
|
@@ -374,19 +435,16 @@ export declare const UOutlet: ReactWebComponent<Outlet, {}>;
|
|
|
374
435
|
|
|
375
436
|
export { }
|
|
376
437
|
|
|
377
|
-
declare global {
|
|
378
|
-
interface
|
|
379
|
-
route:
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
'u-link': Link;
|
|
390
|
-
'u-outlet': Outlet;
|
|
391
|
-
}
|
|
438
|
+
declare global {
|
|
439
|
+
interface WindowEventMap {
|
|
440
|
+
'route-begin': RouteBeginEvent;
|
|
441
|
+
'route-progress': RouteProgressEvent;
|
|
442
|
+
'route-done': RouteDoneEvent;
|
|
443
|
+
'route-error': RouteErrorEvent;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
interface HTMLElementTagNameMap {
|
|
447
|
+
'u-link': Link;
|
|
448
|
+
'u-outlet': Outlet;
|
|
449
|
+
}
|
|
392
450
|
}
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import React from "react";
|
|
|
2
2
|
import { LitElement, html, css, render } from "lit";
|
|
3
3
|
import { state, property, customElement } from "lit/decorators.js";
|
|
4
4
|
import { createRoot } from "react-dom/client";
|
|
5
|
-
import {
|
|
5
|
+
import { unsafeHTML } from "lit-html/directives/unsafe-html.js";
|
|
6
6
|
const e = /* @__PURE__ */ new Set(["children", "localName", "ref", "style", "className"]), n = /* @__PURE__ */ new WeakMap(), t = (e2, t2, o2, l, a) => {
|
|
7
7
|
const s = a?.[t2];
|
|
8
8
|
void 0 === s ? (e2[t2] = o2, null == o2 && t2 in HTMLElement.prototype && e2.removeAttribute(t2)) : o2 !== l && ((e3, t3, o3) => {
|
|
@@ -65,7 +65,9 @@ function parseUrl(url, basepath) {
|
|
|
65
65
|
pathname: urlObj.pathname,
|
|
66
66
|
query: new URLSearchParams(urlObj.search),
|
|
67
67
|
hash: urlObj.hash,
|
|
68
|
-
params: {}
|
|
68
|
+
params: {},
|
|
69
|
+
progress: () => {
|
|
70
|
+
}
|
|
69
71
|
};
|
|
70
72
|
}
|
|
71
73
|
function absolutePath(...paths) {
|
|
@@ -214,7 +216,10 @@ class Outlet extends LitElement {
|
|
|
214
216
|
this.routeId = id;
|
|
215
217
|
this.clear();
|
|
216
218
|
if (!this.container) {
|
|
217
|
-
throw new Error("
|
|
219
|
+
throw new Error("Outlet container is not initialized.");
|
|
220
|
+
}
|
|
221
|
+
if (typeof content !== "object") {
|
|
222
|
+
throw new Error("Content is not a valid renderable object.");
|
|
218
223
|
}
|
|
219
224
|
if (content instanceof HTMLElement) {
|
|
220
225
|
this.container.appendChild(content);
|
|
@@ -292,9 +297,9 @@ class ContentRenderError extends RouteError {
|
|
|
292
297
|
}
|
|
293
298
|
}
|
|
294
299
|
class RouteEvent extends Event {
|
|
295
|
-
constructor(type,
|
|
300
|
+
constructor(type, context, cancelable = false) {
|
|
296
301
|
super(type, { bubbles: true, composed: true, cancelable });
|
|
297
|
-
this.
|
|
302
|
+
this.context = context;
|
|
298
303
|
this.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
299
304
|
}
|
|
300
305
|
/** 이벤트가 취소되었는지 확인 */
|
|
@@ -309,30 +314,72 @@ class RouteEvent extends Event {
|
|
|
309
314
|
}
|
|
310
315
|
}
|
|
311
316
|
class RouteBeginEvent extends RouteEvent {
|
|
312
|
-
constructor(
|
|
313
|
-
super("route-begin",
|
|
317
|
+
constructor(context) {
|
|
318
|
+
super("route-begin", context, false);
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
class RouteProgressEvent extends RouteEvent {
|
|
322
|
+
constructor(context, progress) {
|
|
323
|
+
super("route-progress", context, false);
|
|
324
|
+
this.progress = progress;
|
|
314
325
|
}
|
|
315
326
|
}
|
|
316
327
|
class RouteDoneEvent extends RouteEvent {
|
|
317
|
-
constructor(
|
|
318
|
-
super("route-done",
|
|
328
|
+
constructor(context) {
|
|
329
|
+
super("route-done", context, false);
|
|
319
330
|
}
|
|
320
331
|
}
|
|
321
332
|
class RouteErrorEvent extends RouteEvent {
|
|
322
|
-
constructor(
|
|
323
|
-
super("route-error",
|
|
333
|
+
constructor(context, error) {
|
|
334
|
+
super("route-error", context, false);
|
|
324
335
|
this.error = error;
|
|
325
336
|
}
|
|
326
337
|
}
|
|
327
|
-
const
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
const
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
338
|
+
const ban = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M15 8a6.97 6.97 0 0 0-1.71-4.584l-9.874 9.875A7 7 0 0 0 15 8M2.71 12.584l9.874-9.875a7 7 0 0 0-9.874 9.874ZM16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0"/>\n</svg>';
|
|
339
|
+
const __vite_glob_0_0 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
340
|
+
__proto__: null,
|
|
341
|
+
default: ban
|
|
342
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
343
|
+
const boxSeam = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M8.186 1.113a.5.5 0 0 0-.372 0L1.846 3.5l2.404.961L10.404 2zm3.564 1.426L5.596 5 8 5.961 14.154 3.5zm3.25 1.7-6.5 2.6v7.922l6.5-2.6V4.24zM7.5 14.762V6.838L1 4.239v7.923zM7.443.184a1.5 1.5 0 0 1 1.114 0l7.129 2.852A.5.5 0 0 1 16 3.5v8.662a1 1 0 0 1-.629.928l-7.185 2.874a.5.5 0 0 1-.372 0L.63 13.09a1 1 0 0 1-.63-.928V3.5a.5.5 0 0 1 .314-.464z"/>\n</svg>';
|
|
344
|
+
const __vite_glob_0_1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
345
|
+
__proto__: null,
|
|
346
|
+
default: boxSeam
|
|
347
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
348
|
+
const exclamationTriangle = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M7.938 2.016A.13.13 0 0 1 8.002 2a.13.13 0 0 1 .063.016.15.15 0 0 1 .054.057l6.857 11.667c.036.06.035.124.002.183a.2.2 0 0 1-.054.06.1.1 0 0 1-.066.017H1.146a.1.1 0 0 1-.066-.017.2.2 0 0 1-.054-.06.18.18 0 0 1 .002-.183L7.884 2.073a.15.15 0 0 1 .054-.057m1.044-.45a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767z"/>\n <path d="M7.002 12a1 1 0 1 1 2 0 1 1 0 0 1-2 0M7.1 5.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0z"/>\n</svg>';
|
|
349
|
+
const __vite_glob_0_2 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
350
|
+
__proto__: null,
|
|
351
|
+
default: exclamationTriangle
|
|
352
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
353
|
+
const palette = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M8 5a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3m4 3a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3M5.5 7a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m.5 6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3"/>\n <path d="M16 8c0 3.15-1.866 2.585-3.567 2.07C11.42 9.763 10.465 9.473 10 10c-.603.683-.475 1.819-.351 2.92C9.826 14.495 9.996 16 8 16a8 8 0 1 1 8-8m-8 7c.611 0 .654-.171.655-.176.078-.146.124-.464.07-1.119-.014-.168-.037-.37-.061-.591-.052-.464-.112-1.005-.118-1.462-.01-.707.083-1.61.704-2.314.369-.417.845-.578 1.272-.618.404-.038.812.026 1.16.104.343.077.702.186 1.025.284l.028.008c.346.105.658.199.953.266.653.148.904.083.991.024C14.717 9.38 15 9.161 15 8a7 7 0 1 0-7 7"/>\n</svg>';
|
|
354
|
+
const __vite_glob_0_3 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
355
|
+
__proto__: null,
|
|
356
|
+
default: palette
|
|
357
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
358
|
+
const personLock = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M11 5a3 3 0 1 1-6 0 3 3 0 0 1 6 0M8 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4m0 5.996V14H3s-1 0-1-1 1-4 6-4q.845.002 1.544.107a4.5 4.5 0 0 0-.803.918A11 11 0 0 0 8 10c-2.29 0-3.516.68-4.168 1.332-.678.678-.83 1.418-.832 1.664zM9 13a1 1 0 0 1 1-1v-1a2 2 0 1 1 4 0v1a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-4a1 1 0 0 1-1-1zm3-3a1 1 0 0 0-1 1v1h2v-1a1 1 0 0 0-1-1"/>\n</svg>';
|
|
359
|
+
const __vite_glob_0_4 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
360
|
+
__proto__: null,
|
|
361
|
+
default: personLock
|
|
362
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
363
|
+
const search = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M11.742 10.344a6.5 6.5 0 1 0-1.397 1.398h-.001q.044.06.098.115l3.85 3.85a1 1 0 0 0 1.415-1.414l-3.85-3.85a1 1 0 0 0-.115-.1zM12 6.5a5.5 5.5 0 1 1-11 0 5.5 5.5 0 0 1 11 0"/>\n</svg>';
|
|
364
|
+
const __vite_glob_0_5 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
365
|
+
__proto__: null,
|
|
366
|
+
default: search
|
|
367
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
368
|
+
const stopwatch = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M8.5 5.6a.5.5 0 1 0-1 0v2.9h-3a.5.5 0 0 0 0 1H8a.5.5 0 0 0 .5-.5z"/>\n <path d="M6.5 1A.5.5 0 0 1 7 .5h2a.5.5 0 0 1 0 1v.57c1.36.196 2.594.78 3.584 1.64l.012-.013.354-.354-.354-.353a.5.5 0 0 1 .707-.708l1.414 1.415a.5.5 0 1 1-.707.707l-.353-.354-.354.354-.013.012A7 7 0 1 1 7 2.071V1.5a.5.5 0 0 1-.5-.5M8 3a6 6 0 1 0 .001 12A6 6 0 0 0 8 3"/>\n</svg>';
|
|
369
|
+
const __vite_glob_0_6 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
370
|
+
__proto__: null,
|
|
371
|
+
default: stopwatch
|
|
372
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
373
|
+
const wifiOff = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M10.706 3.294A12.6 12.6 0 0 0 8 3C5.259 3 2.723 3.882.663 5.379a.485.485 0 0 0-.048.736.52.52 0 0 0 .668.05A11.45 11.45 0 0 1 8 4q.946 0 1.852.148zM8 6c-1.905 0-3.68.56-5.166 1.526a.48.48 0 0 0-.063.745.525.525 0 0 0 .652.065 8.45 8.45 0 0 1 3.51-1.27zm2.596 1.404.785-.785q.947.362 1.785.907a.482.482 0 0 1 .063.745.525.525 0 0 1-.652.065 8.5 8.5 0 0 0-1.98-.932zM8 10l.933-.933a6.5 6.5 0 0 1 2.013.637c.285.145.326.524.1.75l-.015.015a.53.53 0 0 1-.611.09A5.5 5.5 0 0 0 8 10m4.905-4.905.747-.747q.886.451 1.685 1.03a.485.485 0 0 1 .047.737.52.52 0 0 1-.668.05 11.5 11.5 0 0 0-1.811-1.07M9.02 11.78c.238.14.236.464.04.66l-.707.706a.5.5 0 0 1-.707 0l-.707-.707c-.195-.195-.197-.518.04-.66A2 2 0 0 1 8 11.5c.374 0 .723.102 1.021.28zm4.355-9.905a.53.53 0 0 1 .75.75l-10.75 10.75a.53.53 0 0 1-.75-.75z"/>\n</svg>';
|
|
374
|
+
const __vite_glob_0_7 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
375
|
+
__proto__: null,
|
|
376
|
+
default: wifiOff
|
|
377
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
378
|
+
const wrenchAdjustable = '<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16">\n <path d="M16 4.5a4.5 4.5 0 0 1-1.703 3.526L13 5l2.959-1.11q.04.3.041.61"/>\n <path d="M11.5 9c.653 0 1.273-.139 1.833-.39L12 5.5 11 3l3.826-1.53A4.5 4.5 0 0 0 7.29 6.092l-6.116 5.096a2.583 2.583 0 1 0 3.638 3.638L9.908 8.71A4.5 4.5 0 0 0 11.5 9m-1.292-4.361-.596.893.809-.27a.25.25 0 0 1 .287.377l-.596.893.809-.27.158.475-1.5.5a.25.25 0 0 1-.287-.376l.596-.893-.809.27a.25.25 0 0 1-.287-.377l.596-.893-.809.27-.158-.475 1.5-.5a.25.25 0 0 1 .287.376M3 14a1 1 0 1 1 0-2 1 1 0 0 1 0 2"/>\n</svg>';
|
|
379
|
+
const __vite_glob_0_8 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
380
|
+
__proto__: null,
|
|
381
|
+
default: wrenchAdjustable
|
|
382
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
336
383
|
const styles = css`
|
|
337
384
|
:host {
|
|
338
385
|
display: flex;
|
|
@@ -392,9 +439,19 @@ var __decorateClass = (decorators, target, key, kind) => {
|
|
|
392
439
|
if (kind && result) __defProp(target, key, result);
|
|
393
440
|
return result;
|
|
394
441
|
};
|
|
395
|
-
const icons = Object.entries(/* @__PURE__ */ Object.assign({
|
|
442
|
+
const icons = Object.entries(/* @__PURE__ */ Object.assign({
|
|
443
|
+
"../assets/ban.svg": __vite_glob_0_0,
|
|
444
|
+
"../assets/box-seam.svg": __vite_glob_0_1,
|
|
445
|
+
"../assets/exclamation-triangle.svg": __vite_glob_0_2,
|
|
446
|
+
"../assets/palette.svg": __vite_glob_0_3,
|
|
447
|
+
"../assets/person-lock.svg": __vite_glob_0_4,
|
|
448
|
+
"../assets/search.svg": __vite_glob_0_5,
|
|
449
|
+
"../assets/stopwatch.svg": __vite_glob_0_6,
|
|
450
|
+
"../assets/wifi-off.svg": __vite_glob_0_7,
|
|
451
|
+
"../assets/wrench-adjustable.svg": __vite_glob_0_8
|
|
452
|
+
})).reduce((acc, [path, content]) => {
|
|
396
453
|
const name = path.split("/").pop()?.replace(".svg", "") || "";
|
|
397
|
-
acc[name] = content;
|
|
454
|
+
acc[name] = content.default;
|
|
398
455
|
return acc;
|
|
399
456
|
}, {});
|
|
400
457
|
let ErrorPage = class extends LitElement {
|
|
@@ -402,7 +459,7 @@ let ErrorPage = class extends LitElement {
|
|
|
402
459
|
const error = this.error || this.getDefaultError();
|
|
403
460
|
const icon = this.getErrorIcon(error.code);
|
|
404
461
|
return html`
|
|
405
|
-
<div class="icon">${icon}</div>
|
|
462
|
+
<div class="icon">${unsafeHTML(icon)}</div>
|
|
406
463
|
<div class="code">${error.code}</div>
|
|
407
464
|
<div class="message">${error.message}</div>
|
|
408
465
|
`;
|
|
@@ -417,25 +474,25 @@ let ErrorPage = class extends LitElement {
|
|
|
417
474
|
const numericCode = typeof code === "string" ? parseInt(code) : code;
|
|
418
475
|
switch (codeStr) {
|
|
419
476
|
case "OUTLET_NOT_FOUND":
|
|
420
|
-
return
|
|
477
|
+
return icons["box-seam"] || "📦";
|
|
421
478
|
case "CONTENT_LOAD_FAILED":
|
|
422
|
-
return
|
|
479
|
+
return icons["wifi-off"] || "📡";
|
|
423
480
|
case "RENDER_FAILED":
|
|
424
|
-
return
|
|
481
|
+
return icons["palette"] || "🎨";
|
|
425
482
|
}
|
|
426
483
|
switch (numericCode) {
|
|
427
484
|
case 404:
|
|
428
|
-
return
|
|
485
|
+
return icons["search"] || "🔍";
|
|
429
486
|
case 403:
|
|
430
|
-
return
|
|
487
|
+
return icons["ban"] || "🚫";
|
|
431
488
|
case 401:
|
|
432
|
-
return
|
|
489
|
+
return icons["person-lock"] || "🔐";
|
|
433
490
|
case 429:
|
|
434
|
-
return
|
|
491
|
+
return icons["stopwatch"] || "⏱️";
|
|
435
492
|
case 503:
|
|
436
|
-
return
|
|
493
|
+
return icons["wrench-adjustable"] || "🛠️";
|
|
437
494
|
default:
|
|
438
|
-
return
|
|
495
|
+
return icons["exclamation-triangle"] || "⚠️";
|
|
439
496
|
}
|
|
440
497
|
}
|
|
441
498
|
};
|
|
@@ -491,51 +548,48 @@ function findAnchorFromEvent(e2) {
|
|
|
491
548
|
function setRoutes(routes, basepath) {
|
|
492
549
|
for (const route of routes) {
|
|
493
550
|
route.id ||= getRandomID();
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
551
|
+
route.ignoreCase ||= false;
|
|
552
|
+
if (route.index === true) {
|
|
553
|
+
route.path = new URLPattern({ pathname: `${basepath}{/}?` }, {
|
|
554
|
+
ignoreCase: route.ignoreCase
|
|
555
|
+
});
|
|
556
|
+
route.force ||= true;
|
|
557
|
+
} else {
|
|
497
558
|
if (typeof route.path === "string") {
|
|
498
559
|
const absolutePathStr = absolutePath(basepath, route.path);
|
|
499
|
-
route.path = new URLPattern({ pathname: `${absolutePathStr}{/}?` }
|
|
560
|
+
route.path = new URLPattern({ pathname: `${absolutePathStr}{/}?` }, {
|
|
561
|
+
ignoreCase: route.ignoreCase
|
|
562
|
+
});
|
|
563
|
+
} else if (route.path instanceof URLPattern) ;
|
|
564
|
+
else {
|
|
565
|
+
route.path = new URLPattern({ pathname: `${basepath}{/}?` }, {
|
|
566
|
+
ignoreCase: route.ignoreCase
|
|
567
|
+
});
|
|
500
568
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
let childBasepath;
|
|
506
|
-
if ("index" in route) {
|
|
507
|
-
childBasepath = basepath;
|
|
569
|
+
if (route.children && route.children.length > 0) {
|
|
570
|
+
const childBasepath = route.path.pathname.replace("{/}?", "");
|
|
571
|
+
route.children = setRoutes(route.children, childBasepath);
|
|
572
|
+
route.force ||= false;
|
|
508
573
|
} else {
|
|
509
|
-
|
|
510
|
-
childBasepath = absolutePath(basepath, route.path);
|
|
511
|
-
} else {
|
|
512
|
-
childBasepath = route.path.pathname.replace("{/}?", "");
|
|
513
|
-
}
|
|
574
|
+
route.force ||= true;
|
|
514
575
|
}
|
|
515
|
-
route.children = setRoutes(route.children, childBasepath);
|
|
516
|
-
route.force ||= false;
|
|
517
|
-
} else {
|
|
518
|
-
route.force ||= true;
|
|
519
576
|
}
|
|
520
577
|
}
|
|
521
578
|
return routes;
|
|
522
579
|
}
|
|
523
580
|
function getRoutes(pathname, routes) {
|
|
524
581
|
for (const route of routes) {
|
|
525
|
-
if (route.children) {
|
|
582
|
+
if (route.index !== true && route.children && route.children.length > 0) {
|
|
526
583
|
const childRoutes = getRoutes(pathname, route.children);
|
|
527
584
|
if (childRoutes.length > 0) {
|
|
528
585
|
return [route, ...childRoutes];
|
|
529
586
|
}
|
|
530
587
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
} else
|
|
535
|
-
|
|
536
|
-
}
|
|
537
|
-
if (matches) {
|
|
538
|
-
return [route];
|
|
588
|
+
if (route.path instanceof URLPattern) {
|
|
589
|
+
const isMatch = route.path.test({ pathname });
|
|
590
|
+
if (isMatch) return [route];
|
|
591
|
+
} else {
|
|
592
|
+
throw new Error("Route path must be an instance of URLPattern, Something wrong in setRoutes function.");
|
|
539
593
|
}
|
|
540
594
|
}
|
|
541
595
|
return [];
|
|
@@ -565,44 +619,36 @@ class Router {
|
|
|
565
619
|
};
|
|
566
620
|
this._rootElement = config.root;
|
|
567
621
|
this._basepath = absolutePath(config.basepath || "/");
|
|
568
|
-
this._routes = setRoutes(config.routes, this._basepath);
|
|
569
|
-
this.
|
|
622
|
+
this._routes = setRoutes(config.routes || [], this._basepath);
|
|
623
|
+
this._fallback = config.fallback;
|
|
570
624
|
window.removeEventListener("popstate", this.handleWindowPopstate);
|
|
571
625
|
window.addEventListener("popstate", this.handleWindowPopstate);
|
|
572
626
|
if (config.useIntercept !== false) {
|
|
573
627
|
this._rootElement.removeEventListener("click", this.handleRootClick);
|
|
574
628
|
this._rootElement.addEventListener("click", this.handleRootClick);
|
|
575
629
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
async waitConnected() {
|
|
579
|
-
let outlet = findOutlet(this._rootElement);
|
|
580
|
-
let count = 0;
|
|
581
|
-
while (!outlet && count < 20) {
|
|
582
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
583
|
-
outlet = findOutlet(this._rootElement);
|
|
584
|
-
count++;
|
|
630
|
+
if (config.initialLoad !== false) {
|
|
631
|
+
void this.go(window.location.href);
|
|
585
632
|
}
|
|
586
|
-
this.handleWindowPopstate();
|
|
587
633
|
}
|
|
588
634
|
/** 객체를 정리하고 이벤트 리스너를 제거합니다. */
|
|
589
635
|
destroy() {
|
|
590
636
|
window.removeEventListener("popstate", this.handleWindowPopstate);
|
|
591
637
|
this._rootElement.removeEventListener("click", this.handleRootClick);
|
|
592
|
-
this._routeInfo = void 0;
|
|
593
638
|
this._requestID = void 0;
|
|
639
|
+
this._context = void 0;
|
|
594
640
|
}
|
|
595
641
|
/** 라우터의 기본 경로 반환 */
|
|
596
642
|
get basepath() {
|
|
597
643
|
return this._basepath;
|
|
598
644
|
}
|
|
599
|
-
/** 등록된 라우트 반환 */
|
|
645
|
+
/** 등록된 라우트 정보 반환 */
|
|
600
646
|
get routes() {
|
|
601
647
|
return this._routes;
|
|
602
648
|
}
|
|
603
649
|
/** 현재 라우팅 정보 반환 */
|
|
604
|
-
get
|
|
605
|
-
return this.
|
|
650
|
+
get context() {
|
|
651
|
+
return this._context;
|
|
606
652
|
}
|
|
607
653
|
/**
|
|
608
654
|
* 지정한 경로의 클라이언트 라우팅을 수행합니다. 상대경로일 경우 basepath와 조합되어 이동합니다.
|
|
@@ -611,31 +657,43 @@ class Router {
|
|
|
611
657
|
async go(href) {
|
|
612
658
|
const requestID = getRandomID();
|
|
613
659
|
this._requestID = requestID;
|
|
614
|
-
const
|
|
615
|
-
if (
|
|
660
|
+
const context = parseUrl(href, this._basepath);
|
|
661
|
+
if (context.href !== window.location.href) {
|
|
662
|
+
window.history.pushState({ basepath: context.basepath }, "", context.href);
|
|
663
|
+
} else {
|
|
664
|
+
window.history.replaceState({ basepath: context.basepath }, "", context.href);
|
|
665
|
+
}
|
|
666
|
+
const progressCallback = (value) => {
|
|
667
|
+
if (this._requestID !== requestID) return;
|
|
668
|
+
const progress = Math.max(0, Math.min(100, Math.round(value)));
|
|
669
|
+
window.dispatchEvent(new RouteProgressEvent(context, progress));
|
|
670
|
+
};
|
|
671
|
+
context.progress = progressCallback;
|
|
616
672
|
let outlet = void 0;
|
|
617
673
|
try {
|
|
618
674
|
if (this._requestID !== requestID) return;
|
|
619
|
-
window.dispatchEvent(new RouteBeginEvent(
|
|
620
|
-
const routes = getRoutes(
|
|
675
|
+
window.dispatchEvent(new RouteBeginEvent(context));
|
|
676
|
+
const routes = getRoutes(context.pathname, this._routes);
|
|
621
677
|
const lastRoute = routes[routes.length - 1];
|
|
622
|
-
if (lastRoute &&
|
|
623
|
-
|
|
678
|
+
if (lastRoute && lastRoute.path instanceof URLPattern) {
|
|
679
|
+
context.params = lastRoute.path.exec({ pathname: context.pathname })?.pathname.groups || {};
|
|
624
680
|
}
|
|
625
|
-
this.
|
|
626
|
-
window.route = routeInfo;
|
|
681
|
+
this._context = context;
|
|
627
682
|
outlet = findOutletOrThrow(this._rootElement);
|
|
628
683
|
let title = void 0;
|
|
629
684
|
let content = null;
|
|
630
685
|
let element = null;
|
|
631
686
|
if (routes.length === 0) {
|
|
632
|
-
throw new NotFoundError(
|
|
687
|
+
throw new NotFoundError(context.href);
|
|
633
688
|
}
|
|
634
689
|
for (const route of routes) {
|
|
635
690
|
if (this._requestID !== requestID) return;
|
|
636
691
|
if (!route.render) continue;
|
|
637
692
|
try {
|
|
638
|
-
content = await route.render(
|
|
693
|
+
content = await route.render(context);
|
|
694
|
+
if (content === false || content === void 0 || content === null) {
|
|
695
|
+
throw new Error("Failed to load content for the route.");
|
|
696
|
+
}
|
|
639
697
|
} catch (LoadError) {
|
|
640
698
|
throw new ContentLoadError(LoadError);
|
|
641
699
|
}
|
|
@@ -648,29 +706,29 @@ class Router {
|
|
|
648
706
|
title = route.title || title;
|
|
649
707
|
}
|
|
650
708
|
document.title = title || document.title;
|
|
651
|
-
|
|
652
|
-
if (routeInfo.href !== window.location.href) {
|
|
653
|
-
window.history.pushState({ basepath: routeInfo.basepath }, "", routeInfo.href);
|
|
654
|
-
} else {
|
|
655
|
-
window.history.replaceState({ basepath: routeInfo.basepath }, "", routeInfo.href);
|
|
656
|
-
}
|
|
657
|
-
window.dispatchEvent(new RouteDoneEvent(routeInfo));
|
|
709
|
+
window.dispatchEvent(new RouteDoneEvent(context));
|
|
658
710
|
} catch (error) {
|
|
659
711
|
const routeError = error instanceof RouteError ? error : new RouteError(
|
|
660
712
|
error.status || error.code || "UNKNOWN_ERROR",
|
|
661
713
|
error.message || "An unexpected error occurred",
|
|
662
714
|
error
|
|
663
715
|
);
|
|
664
|
-
window.dispatchEvent(new RouteErrorEvent(
|
|
665
|
-
console.error("Routing error:",
|
|
716
|
+
window.dispatchEvent(new RouteErrorEvent(context, routeError));
|
|
717
|
+
console.error("Routing error:", routeError.original);
|
|
666
718
|
try {
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
719
|
+
if (this._fallback && this._fallback.render && outlet) {
|
|
720
|
+
const fallbackContent = await this._fallback.render({ ...context, error: routeError });
|
|
721
|
+
outlet.renderContent({ id: "#fallback", content: fallbackContent, force: true });
|
|
722
|
+
document.title = this._fallback.title || document.title;
|
|
671
723
|
} else {
|
|
672
|
-
|
|
673
|
-
|
|
724
|
+
const errorContent = new ErrorPage();
|
|
725
|
+
errorContent.error = error;
|
|
726
|
+
if (outlet) {
|
|
727
|
+
outlet.renderContent({ id: "#error", content: errorContent, force: true });
|
|
728
|
+
} else {
|
|
729
|
+
document.body.innerHTML = "";
|
|
730
|
+
document.body.appendChild(errorContent);
|
|
731
|
+
}
|
|
674
732
|
}
|
|
675
733
|
} catch (pageError) {
|
|
676
734
|
console.error("Failed to render error component:", pageError);
|
|
@@ -690,6 +748,7 @@ export {
|
|
|
690
748
|
RouteDoneEvent,
|
|
691
749
|
RouteError,
|
|
692
750
|
RouteErrorEvent,
|
|
751
|
+
RouteProgressEvent,
|
|
693
752
|
Router,
|
|
694
753
|
ULink,
|
|
695
754
|
UOutlet
|
package/package.json
CHANGED
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@iyulab/router",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "A modern client-side router for web applications with support for Lit and React components",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"lit",
|
|
7
|
-
"react",
|
|
8
|
-
"router",
|
|
9
|
-
"routing",
|
|
10
|
-
"spa",
|
|
11
|
-
"navigation",
|
|
12
|
-
"client-side"
|
|
13
|
-
],
|
|
14
|
-
"license": "MIT",
|
|
15
|
-
"author": "iyulab",
|
|
16
|
-
"repository": {
|
|
17
|
-
"type": "git",
|
|
18
|
-
"url": "https://github.com/iyulab/node-router.git"
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist",
|
|
22
|
-
"package.json",
|
|
23
|
-
"README.md",
|
|
24
|
-
"LICENSE"
|
|
25
|
-
],
|
|
26
|
-
"type": "module",
|
|
27
|
-
"types": "dist/index.d.ts",
|
|
28
|
-
"exports": {
|
|
29
|
-
".": {
|
|
30
|
-
"types": "./dist/index.d.ts",
|
|
31
|
-
"import": "./dist/index.js"
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
"scripts": {
|
|
35
|
-
"build": "vite build"
|
|
36
|
-
},
|
|
37
|
-
"dependencies": {
|
|
38
|
-
"lit": "^3.3.1",
|
|
39
|
-
"react": "^19.2.
|
|
40
|
-
"react-dom": "^19.2.
|
|
41
|
-
},
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"@lit/react": "^1.0.8",
|
|
44
|
-
"@types/node": "^24.10.1",
|
|
45
|
-
"@types/react": "^19.2.
|
|
46
|
-
"@types/react-dom": "^19.2.3",
|
|
47
|
-
"typescript": "^5.9.3",
|
|
48
|
-
"vite": "^7.2.
|
|
49
|
-
"vite-plugin-dts": "^4.5.4"
|
|
50
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@iyulab/router",
|
|
3
|
+
"version": "0.5.3",
|
|
4
|
+
"description": "A modern client-side router for web applications with support for Lit and React components",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"lit",
|
|
7
|
+
"react",
|
|
8
|
+
"router",
|
|
9
|
+
"routing",
|
|
10
|
+
"spa",
|
|
11
|
+
"navigation",
|
|
12
|
+
"client-side"
|
|
13
|
+
],
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"author": "iyulab",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/iyulab/node-router.git"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"package.json",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"types": "dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"scripts": {
|
|
35
|
+
"build": "vite build"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"lit": "^3.3.1",
|
|
39
|
+
"react": "^19.2.1",
|
|
40
|
+
"react-dom": "^19.2.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@lit/react": "^1.0.8",
|
|
44
|
+
"@types/node": "^24.10.1",
|
|
45
|
+
"@types/react": "^19.2.7",
|
|
46
|
+
"@types/react-dom": "^19.2.3",
|
|
47
|
+
"typescript": "^5.9.3",
|
|
48
|
+
"vite": "^7.2.6",
|
|
49
|
+
"vite-plugin-dts": "^4.5.4"
|
|
50
|
+
}
|
|
51
51
|
}
|