@iyulab/router 0.5.2 → 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 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
@@ -26,7 +26,6 @@ declare interface BaseRouteConfig {
26
26
  * - "/user/:id/:name?"
27
27
  * - "/user/:id/:name*"
28
28
  * - "/user/:id/:name+"
29
- * - "/user/:id/:name{1,3}"
30
29
  * @link
31
30
  * https://developer.mozilla.org/en-US/docs/Web/API/URLPattern
32
31
  */
@@ -56,6 +55,11 @@ declare interface BaseRouteConfig {
56
55
  * - true로 설정하면 기존 렌더링을 무시하고 새로 렌더링합니다.
57
56
  */
58
57
  force?: boolean;
58
+ /**
59
+ * 경로 매칭시 대소문자 구분 여부
60
+ * @default false
61
+ */
62
+ ignoreCase?: boolean;
59
63
  }
60
64
 
61
65
  /**
@@ -431,16 +435,16 @@ export declare const UOutlet: ReactWebComponent<Outlet, {}>;
431
435
 
432
436
  export { }
433
437
 
434
- declare global {
435
- interface WindowEventMap {
436
- 'route-begin': RouteBeginEvent;
437
- 'route-progress': RouteProgressEvent;
438
- 'route-done': RouteDoneEvent;
439
- 'route-error': RouteErrorEvent;
440
- }
441
-
442
- interface HTMLElementTagNameMap {
443
- 'u-link': Link;
444
- 'u-outlet': Outlet;
445
- }
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
+ }
446
450
  }
package/dist/index.js CHANGED
@@ -218,8 +218,8 @@ class Outlet extends LitElement {
218
218
  if (!this.container) {
219
219
  throw new Error("Outlet container is not initialized.");
220
220
  }
221
- if (content === false) {
222
- throw new Error("Content is false, cannot render.");
221
+ if (typeof content !== "object") {
222
+ throw new Error("Content is not a valid renderable object.");
223
223
  }
224
224
  if (content instanceof HTMLElement) {
225
225
  this.container.appendChild(content);
@@ -548,16 +548,23 @@ function findAnchorFromEvent(e2) {
548
548
  function setRoutes(routes, basepath) {
549
549
  for (const route of routes) {
550
550
  route.id ||= getRandomID();
551
+ route.ignoreCase ||= false;
551
552
  if (route.index === true) {
552
- route.path = new URLPattern({ pathname: `${basepath}{/}?` });
553
+ route.path = new URLPattern({ pathname: `${basepath}{/}?` }, {
554
+ ignoreCase: route.ignoreCase
555
+ });
553
556
  route.force ||= true;
554
557
  } else {
555
558
  if (typeof route.path === "string") {
556
559
  const absolutePathStr = absolutePath(basepath, route.path);
557
- route.path = new URLPattern({ pathname: `${absolutePathStr}{/}?` });
560
+ route.path = new URLPattern({ pathname: `${absolutePathStr}{/}?` }, {
561
+ ignoreCase: route.ignoreCase
562
+ });
558
563
  } else if (route.path instanceof URLPattern) ;
559
564
  else {
560
- route.path = new URLPattern({ pathname: `${basepath}{/}?` });
565
+ route.path = new URLPattern({ pathname: `${basepath}{/}?` }, {
566
+ ignoreCase: route.ignoreCase
567
+ });
561
568
  }
562
569
  if (route.children && route.children.length > 0) {
563
570
  const childBasepath = route.path.pathname.replace("{/}?", "");
@@ -651,18 +658,17 @@ class Router {
651
658
  const requestID = getRandomID();
652
659
  this._requestID = requestID;
653
660
  const context = parseUrl(href, this._basepath);
654
- if (context.href === this._context?.href) return;
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
+ }
655
666
  const progressCallback = (value) => {
656
667
  if (this._requestID !== requestID) return;
657
668
  const progress = Math.max(0, Math.min(100, Math.round(value)));
658
669
  window.dispatchEvent(new RouteProgressEvent(context, progress));
659
670
  };
660
671
  context.progress = progressCallback;
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
672
  let outlet = void 0;
667
673
  try {
668
674
  if (this._requestID !== requestID) return;
@@ -685,13 +691,12 @@ class Router {
685
691
  if (!route.render) continue;
686
692
  try {
687
693
  content = await route.render(context);
688
- if (content === false || content === null) {
694
+ if (content === false || content === void 0 || content === null) {
689
695
  throw new Error("Failed to load content for the route.");
690
696
  }
691
697
  } catch (LoadError) {
692
698
  throw new ContentLoadError(LoadError);
693
699
  }
694
- if (this._requestID !== requestID) return;
695
700
  try {
696
701
  element = await outlet.renderContent({ id: route.id, content, force: route.force });
697
702
  } catch (renderError) {
package/package.json CHANGED
@@ -1,51 +1,51 @@
1
- {
2
- "name": "@iyulab/router",
3
- "version": "0.5.2",
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.0",
40
- "react-dom": "^19.2.0"
41
- },
42
- "devDependencies": {
43
- "@lit/react": "^1.0.8",
44
- "@types/node": "^24.10.1",
45
- "@types/react": "^19.2.5",
46
- "@types/react-dom": "^19.2.3",
47
- "typescript": "^5.9.3",
48
- "vite": "^7.2.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
  }