@constela/router 8.0.0 → 10.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +212 -0
- package/package.json +5 -5
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# @constela/router
|
|
2
|
+
|
|
3
|
+
Client-side routing for Constela applications using the History API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @constela/router
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
**Peer Dependencies:**
|
|
12
|
+
- `@constela/compiler` ^0.7.0
|
|
13
|
+
- `@constela/runtime` ^0.10.1
|
|
14
|
+
|
|
15
|
+
## Route Definition
|
|
16
|
+
|
|
17
|
+
Define routes in your JSON program:
|
|
18
|
+
|
|
19
|
+
```json
|
|
20
|
+
{
|
|
21
|
+
"route": {
|
|
22
|
+
"path": "/users/:id",
|
|
23
|
+
"title": { "expr": "bin", "op": "+", "left": { "expr": "lit", "value": "User: " }, "right": { "expr": "route", "name": "id" } },
|
|
24
|
+
"layout": "MainLayout",
|
|
25
|
+
"meta": {
|
|
26
|
+
"description": { "expr": "lit", "value": "User profile page" }
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"state": { ... },
|
|
30
|
+
"actions": [ ... ],
|
|
31
|
+
"view": { ... }
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Accessing Route Parameters
|
|
36
|
+
|
|
37
|
+
Use the `route` expression in your JSON:
|
|
38
|
+
|
|
39
|
+
**URL parameter** (e.g., `/users/123` → `"123"`):
|
|
40
|
+
```json
|
|
41
|
+
{ "expr": "route", "name": "id", "source": "param" }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Query string parameter** (e.g., `?tab=settings` → `"settings"`):
|
|
45
|
+
```json
|
|
46
|
+
{ "expr": "route", "name": "tab", "source": "query" }
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Full path**:
|
|
50
|
+
```json
|
|
51
|
+
{ "expr": "route", "source": "path" }
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Route Patterns
|
|
55
|
+
|
|
56
|
+
| Pattern | Example Path | Params |
|
|
57
|
+
|---------|--------------|--------|
|
|
58
|
+
| `/` | `/` | `{}` |
|
|
59
|
+
| `/about` | `/about` | `{}` |
|
|
60
|
+
| `/users/:id` | `/users/123` | `{ id: "123" }` |
|
|
61
|
+
| `/posts/:id/comments/:cid` | `/posts/1/comments/5` | `{ id: "1", cid: "5" }` |
|
|
62
|
+
| `/docs/*` | `/docs/getting-started/intro` | Catch-all |
|
|
63
|
+
|
|
64
|
+
## File-Based Routing
|
|
65
|
+
|
|
66
|
+
With `@constela/start`, routes are derived from file paths:
|
|
67
|
+
|
|
68
|
+
| File | Route |
|
|
69
|
+
|------|-------|
|
|
70
|
+
| `src/routes/index.json` | `/` |
|
|
71
|
+
| `src/routes/about.json` | `/about` |
|
|
72
|
+
| `src/routes/users/[id].json` | `/users/:id` |
|
|
73
|
+
| `src/routes/docs/[...slug].json` | `/docs/*` |
|
|
74
|
+
|
|
75
|
+
## Example: Dynamic Page
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"version": "1.0",
|
|
80
|
+
"route": {
|
|
81
|
+
"path": "/users/:id"
|
|
82
|
+
},
|
|
83
|
+
"state": {
|
|
84
|
+
"user": { "type": "object", "initial": null },
|
|
85
|
+
"loading": { "type": "boolean", "initial": true }
|
|
86
|
+
},
|
|
87
|
+
"lifecycle": {
|
|
88
|
+
"onMount": "fetchUser"
|
|
89
|
+
},
|
|
90
|
+
"actions": [
|
|
91
|
+
{
|
|
92
|
+
"name": "fetchUser",
|
|
93
|
+
"steps": [
|
|
94
|
+
{
|
|
95
|
+
"do": "fetch",
|
|
96
|
+
"url": {
|
|
97
|
+
"expr": "bin",
|
|
98
|
+
"op": "+",
|
|
99
|
+
"left": { "expr": "lit", "value": "https://api.example.com/users/" },
|
|
100
|
+
"right": { "expr": "route", "name": "id" }
|
|
101
|
+
},
|
|
102
|
+
"method": "GET",
|
|
103
|
+
"onSuccess": [
|
|
104
|
+
{ "do": "set", "target": "user", "value": { "expr": "var", "name": "data" } },
|
|
105
|
+
{ "do": "set", "target": "loading", "value": { "expr": "lit", "value": false } }
|
|
106
|
+
]
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
],
|
|
111
|
+
"view": {
|
|
112
|
+
"kind": "if",
|
|
113
|
+
"condition": { "expr": "state", "name": "loading" },
|
|
114
|
+
"then": { "kind": "text", "value": { "expr": "lit", "value": "Loading..." } },
|
|
115
|
+
"else": {
|
|
116
|
+
"kind": "element",
|
|
117
|
+
"tag": "h1",
|
|
118
|
+
"children": [
|
|
119
|
+
{ "kind": "text", "value": { "expr": "get", "base": { "expr": "state", "name": "user" }, "path": "name" } }
|
|
120
|
+
]
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Navigation
|
|
127
|
+
|
|
128
|
+
Navigate using the `navigate` action step:
|
|
129
|
+
|
|
130
|
+
**Basic navigation**:
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"name": "goToProfile",
|
|
134
|
+
"steps": [
|
|
135
|
+
{ "do": "navigate", "url": { "expr": "lit", "value": "/profile" } }
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**With dynamic URL**:
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"name": "goToUser",
|
|
144
|
+
"steps": [
|
|
145
|
+
{
|
|
146
|
+
"do": "navigate",
|
|
147
|
+
"url": {
|
|
148
|
+
"expr": "bin",
|
|
149
|
+
"op": "+",
|
|
150
|
+
"left": { "expr": "lit", "value": "/users/" },
|
|
151
|
+
"right": { "expr": "state", "name": "selectedUserId" }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Replace history** (no back button):
|
|
159
|
+
```json
|
|
160
|
+
{
|
|
161
|
+
"do": "navigate",
|
|
162
|
+
"url": { "expr": "lit", "value": "/login" },
|
|
163
|
+
"replace": true
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Internal API
|
|
168
|
+
|
|
169
|
+
> For framework developers only. End users should use the CLI.
|
|
170
|
+
|
|
171
|
+
### createRouter
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
import { createRouter } from '@constela/router';
|
|
175
|
+
|
|
176
|
+
const router = createRouter({
|
|
177
|
+
routes: [
|
|
178
|
+
{ path: '/', program: homeProgram, title: 'Home' },
|
|
179
|
+
{ path: '/about', program: aboutProgram, title: 'About' },
|
|
180
|
+
{ path: '/users/:id', program: userProgram, title: (ctx) => `User ${ctx.params.id}` },
|
|
181
|
+
],
|
|
182
|
+
basePath: '/app',
|
|
183
|
+
fallback: notFoundProgram,
|
|
184
|
+
onRouteChange: (ctx) => console.log('Route changed:', ctx.path),
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const { destroy } = router.mount(document.getElementById('app'));
|
|
188
|
+
router.navigate('/about');
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Helper Functions
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { bindLink, createLink, matchRoute, parseParams } from '@constela/router';
|
|
195
|
+
|
|
196
|
+
// Bind existing anchor to router
|
|
197
|
+
bindLink(router, document.querySelector('a[href="/about"]'), '/about');
|
|
198
|
+
|
|
199
|
+
// Create router-aware anchor
|
|
200
|
+
const link = createLink(router, '/users/123', 'View User');
|
|
201
|
+
|
|
202
|
+
// Match route pattern
|
|
203
|
+
const match = matchRoute('/users/:id', '/users/123'); // { id: '123' }
|
|
204
|
+
|
|
205
|
+
// Parse params from path
|
|
206
|
+
const params = parseParams('/users/:id/posts/:postId', '/users/123/posts/456');
|
|
207
|
+
// { id: '123', postId: '456' }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@constela/router",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.0",
|
|
4
4
|
"description": "Client-side routing for Constela applications",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {},
|
|
18
18
|
"peerDependencies": {
|
|
19
|
-
"@constela/compiler": "^0.
|
|
20
|
-
"@constela/runtime": "^0.
|
|
19
|
+
"@constela/compiler": "^0.9.0",
|
|
20
|
+
"@constela/runtime": "^0.12.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@types/node": "^20.10.0",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"tsup": "^8.0.0",
|
|
26
26
|
"typescript": "^5.3.0",
|
|
27
27
|
"vitest": "^2.0.0",
|
|
28
|
-
"@constela/
|
|
29
|
-
"@constela/
|
|
28
|
+
"@constela/runtime": "0.12.0",
|
|
29
|
+
"@constela/compiler": "0.9.0"
|
|
30
30
|
},
|
|
31
31
|
"engines": {
|
|
32
32
|
"node": ">=20.0.0"
|