@real-router/angular 0.0.1
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 +454 -0
- package/dist/README.md +454 -0
- package/dist/fesm2022/real-router-angular.mjs +575 -0
- package/dist/fesm2022/real-router-angular.mjs.map +1 -0
- package/dist/types/real-router-angular.d.ts +134 -0
- package/dist/types/real-router-angular.d.ts.map +1 -0
- package/package.json +64 -0
- package/src/components/NavigationAnnouncer.ts +18 -0
- package/src/components/RouteView.ts +102 -0
- package/src/components/RouterErrorBoundary.ts +88 -0
- package/src/directives/RealLink.ts +97 -0
- package/src/directives/RealLinkActive.ts +68 -0
- package/src/directives/RouteMatch.ts +7 -0
- package/src/directives/RouteNotFound.ts +6 -0
- package/src/dom-utils/CLAUDE.md +76 -0
- package/src/dom-utils/index.ts +11 -0
- package/src/dom-utils/link-utils.ts +121 -0
- package/src/dom-utils/route-announcer.ts +159 -0
- package/src/functions/index.ts +13 -0
- package/src/functions/injectIsActiveRoute.ts +21 -0
- package/src/functions/injectNavigator.ts +8 -0
- package/src/functions/injectOrThrow.ts +15 -0
- package/src/functions/injectRoute.ts +8 -0
- package/src/functions/injectRouteNode.ts +16 -0
- package/src/functions/injectRouteUtils.ts +12 -0
- package/src/functions/injectRouter.ts +8 -0
- package/src/functions/injectRouterTransition.ts +14 -0
- package/src/index.ts +39 -0
- package/src/providers.ts +33 -0
- package/src/sourceToSignal.ts +20 -0
- package/src/types.ts +8 -0
package/README.md
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
# @real-router/angular
|
|
2
|
+
|
|
3
|
+
[](../../LICENSE)
|
|
4
|
+
|
|
5
|
+
> Angular 21 integration for [Real-Router](https://github.com/greydragon888/real-router) — inject functions, components, and directives.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @real-router/angular @real-router/core @real-router/browser-plugin
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
**Peer dependencies:** `@angular/core` >= 21.0.0, `@angular/common` >= 21.0.0
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
Bootstrap a standalone Angular application with `provideRealRouter`:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { bootstrapApplication } from "@angular/platform-browser";
|
|
21
|
+
import { createRouter } from "@real-router/core";
|
|
22
|
+
import { browserPluginFactory } from "@real-router/browser-plugin";
|
|
23
|
+
import { provideRealRouter } from "@real-router/angular";
|
|
24
|
+
import { AppComponent } from "./app.component";
|
|
25
|
+
|
|
26
|
+
const router = createRouter([
|
|
27
|
+
{ name: "home", path: "/" },
|
|
28
|
+
{
|
|
29
|
+
name: "users",
|
|
30
|
+
path: "/users",
|
|
31
|
+
children: [{ name: "profile", path: "/:id" }],
|
|
32
|
+
},
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
router.usePlugin(browserPluginFactory());
|
|
36
|
+
await router.start();
|
|
37
|
+
|
|
38
|
+
bootstrapApplication(AppComponent, {
|
|
39
|
+
providers: [provideRealRouter(router)],
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Then use `injectRoute` and `RouteView` in your root component:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
import { Component } from "@angular/core";
|
|
47
|
+
import {
|
|
48
|
+
injectRoute,
|
|
49
|
+
RouteView,
|
|
50
|
+
RouteMatch,
|
|
51
|
+
RouteNotFound,
|
|
52
|
+
RealLink,
|
|
53
|
+
} from "@real-router/angular";
|
|
54
|
+
|
|
55
|
+
@Component({
|
|
56
|
+
selector: "app-root",
|
|
57
|
+
imports: [RouteView, RouteMatch, RouteNotFound, RealLink],
|
|
58
|
+
template: `
|
|
59
|
+
<nav>
|
|
60
|
+
<a realLink routeName="home">Home</a>
|
|
61
|
+
<a realLink routeName="users">Users</a>
|
|
62
|
+
</nav>
|
|
63
|
+
|
|
64
|
+
<route-view [routeNode]="''">
|
|
65
|
+
<ng-template routeMatch="home">
|
|
66
|
+
<app-home />
|
|
67
|
+
</ng-template>
|
|
68
|
+
<ng-template routeMatch="users">
|
|
69
|
+
<app-users-layout />
|
|
70
|
+
</ng-template>
|
|
71
|
+
<ng-template routeNotFound>
|
|
72
|
+
<app-not-found />
|
|
73
|
+
</ng-template>
|
|
74
|
+
</route-view>
|
|
75
|
+
`,
|
|
76
|
+
})
|
|
77
|
+
export class AppComponent {
|
|
78
|
+
readonly route = injectRoute();
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
For nested children (e.g., `users.profile`), place another `<route-view>` inside the parent layout and set `routeNode` to the parent's name:
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
@Component({
|
|
86
|
+
selector: "app-users-layout",
|
|
87
|
+
imports: [RouteView, RouteMatch],
|
|
88
|
+
template: `
|
|
89
|
+
<h1>Users</h1>
|
|
90
|
+
<route-view [routeNode]="'users'">
|
|
91
|
+
<ng-template routeMatch="profile">
|
|
92
|
+
<app-user-profile />
|
|
93
|
+
</ng-template>
|
|
94
|
+
</route-view>
|
|
95
|
+
`,
|
|
96
|
+
})
|
|
97
|
+
export class UsersLayoutComponent {}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Functions
|
|
101
|
+
|
|
102
|
+
All inject functions must be called within an injection context (constructor, field initializer, or `runInInjectionContext`). Route state functions return `RouteSignals` — an object with a `routeState` signal and a stable `navigator` reference.
|
|
103
|
+
|
|
104
|
+
| Function | Returns | Reactive? |
|
|
105
|
+
| ------------------------------------------- | ---------------------------------- | ------------------------------------ |
|
|
106
|
+
| `injectRouter()` | `Router` | Never |
|
|
107
|
+
| `injectNavigator()` | `Navigator` | Never |
|
|
108
|
+
| `injectRoute()` | `RouteSignals` | `routeState` on every navigation |
|
|
109
|
+
| `injectRouteNode(name)` | `RouteSignals` | When the node subtree is entered, left, or changes between descendants (uses `shouldUpdateNode` — sibling-leaf transitions within the same subtree still fire) |
|
|
110
|
+
| `injectRouteUtils()` | `RouteUtils` | Never |
|
|
111
|
+
| `injectRouterTransition()` | `Signal<RouterTransitionSnapshot>` | On transition start/end |
|
|
112
|
+
| `injectIsActiveRoute(name, params?, opts?)` | `Signal<boolean>` | On active state change |
|
|
113
|
+
|
|
114
|
+
`RouteSignals` shape:
|
|
115
|
+
|
|
116
|
+
```typescript
|
|
117
|
+
interface RouteSignals {
|
|
118
|
+
readonly routeState: Signal<RouteSnapshot>; // { route, previousRoute }
|
|
119
|
+
readonly navigator: Navigator;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// injectRouteNode — updates only when "users.*" changes
|
|
125
|
+
@Component({
|
|
126
|
+
selector: "app-users-layout",
|
|
127
|
+
template: `
|
|
128
|
+
@if (route.routeState().route; as r) {
|
|
129
|
+
@switch (r.name) {
|
|
130
|
+
@case ("users") {
|
|
131
|
+
<app-users-list />
|
|
132
|
+
}
|
|
133
|
+
@case ("users.profile") {
|
|
134
|
+
<app-user-profile [id]="r.params['id']" />
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
`,
|
|
139
|
+
})
|
|
140
|
+
export class UsersLayoutComponent {
|
|
141
|
+
readonly route = injectRouteNode("users");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// injectNavigator — stable reference, never reactive
|
|
145
|
+
@Component({
|
|
146
|
+
selector: "app-back-button",
|
|
147
|
+
template: `<button (click)="goHome()">Back</button>`,
|
|
148
|
+
})
|
|
149
|
+
export class BackButtonComponent {
|
|
150
|
+
private readonly navigator = injectNavigator();
|
|
151
|
+
|
|
152
|
+
goHome(): void {
|
|
153
|
+
this.navigator.navigate("home");
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// injectRouterTransition — progress bars, loading states
|
|
158
|
+
@Component({
|
|
159
|
+
selector: "app-progress",
|
|
160
|
+
template: `
|
|
161
|
+
@if (transition().isTransitioning) {
|
|
162
|
+
<div class="progress-bar"></div>
|
|
163
|
+
}
|
|
164
|
+
`,
|
|
165
|
+
})
|
|
166
|
+
export class ProgressComponent {
|
|
167
|
+
readonly transition = injectRouterTransition();
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Components
|
|
172
|
+
|
|
173
|
+
### `<route-view>`
|
|
174
|
+
|
|
175
|
+
Declarative route matching. Renders the first `ng-template[routeMatch]` whose segment matches the active route.
|
|
176
|
+
|
|
177
|
+
```html
|
|
178
|
+
<route-view [routeNode]="''">
|
|
179
|
+
<ng-template routeMatch="users">
|
|
180
|
+
<app-users />
|
|
181
|
+
</ng-template>
|
|
182
|
+
<ng-template routeMatch="settings">
|
|
183
|
+
<app-settings />
|
|
184
|
+
</ng-template>
|
|
185
|
+
<ng-template routeNotFound>
|
|
186
|
+
<app-not-found />
|
|
187
|
+
</ng-template>
|
|
188
|
+
</route-view>
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The `routeNode` input (aliased from `nodeName`) scopes the view to a subtree. Pass `""` for the root level, or a route name like `"users"` for nested layouts:
|
|
192
|
+
|
|
193
|
+
```html
|
|
194
|
+
<!-- Nested layout: renders when any "users.*" route is active -->
|
|
195
|
+
<route-view [routeNode]="'users'">
|
|
196
|
+
<ng-template routeMatch="profile">
|
|
197
|
+
<app-user-profile />
|
|
198
|
+
</ng-template>
|
|
199
|
+
</route-view>
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
> **Note:** The input is named `routeNode` (not `nodeName`) because `nodeName` is a read-only property on `HTMLElement`. Angular's template binding would fail with the unaliased name.
|
|
203
|
+
|
|
204
|
+
### `<router-error-boundary>`
|
|
205
|
+
|
|
206
|
+
Declarative error handling for navigation errors. Renders its content normally and shows an error template alongside it when a guard rejects or a route is not found.
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import { RouterErrorBoundary, type ErrorContext } from "@real-router/angular";
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
```html
|
|
213
|
+
<router-error-boundary
|
|
214
|
+
[errorTemplate]="errorTpl"
|
|
215
|
+
(onError)="onNavError($event)"
|
|
216
|
+
>
|
|
217
|
+
<a realLink routeName="protected">Go to Protected</a>
|
|
218
|
+
</router-error-boundary>
|
|
219
|
+
|
|
220
|
+
<ng-template #errorTpl let-error let-reset="resetError">
|
|
221
|
+
<div class="toast">
|
|
222
|
+
{{ error.code }}
|
|
223
|
+
<button (click)="reset()">Dismiss</button>
|
|
224
|
+
</div>
|
|
225
|
+
</ng-template>
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
The template context is typed as `ErrorContext`:
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
interface ErrorContext {
|
|
232
|
+
$implicit: RouterError; // the navigation error
|
|
233
|
+
resetError: () => void; // dismiss the error
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Auto-resets on the next successful navigation. Works with both `realLink` and imperative `router.navigate()`.
|
|
238
|
+
|
|
239
|
+
### `<navigation-announcer>`
|
|
240
|
+
|
|
241
|
+
WCAG-compliant screen reader announcements for route changes. Add it once near the root of your application:
|
|
242
|
+
|
|
243
|
+
```html
|
|
244
|
+
<navigation-announcer />
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
See the [Accessibility](#accessibility) section for details.
|
|
248
|
+
|
|
249
|
+
## Directives
|
|
250
|
+
|
|
251
|
+
### `realLink`
|
|
252
|
+
|
|
253
|
+
Navigation directive for `<a>` elements. Handles click events, sets `href`, and applies an active CSS class automatically.
|
|
254
|
+
|
|
255
|
+
```html
|
|
256
|
+
<a realLink routeName="users.profile" [routeParams]="{ id: '123' }">
|
|
257
|
+
View Profile
|
|
258
|
+
</a>
|
|
259
|
+
|
|
260
|
+
<a
|
|
261
|
+
realLink
|
|
262
|
+
routeName="users.profile"
|
|
263
|
+
[routeParams]="{ id: '123' }"
|
|
264
|
+
activeClassName="is-active"
|
|
265
|
+
[activeStrict]="false"
|
|
266
|
+
[ignoreQueryParams]="true"
|
|
267
|
+
[routeOptions]="{ replace: true }"
|
|
268
|
+
>
|
|
269
|
+
View Profile
|
|
270
|
+
</a>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
| Input | Type | Default | Description |
|
|
274
|
+
| ------------------- | ------------------- | ---------- | -------------------------------------- |
|
|
275
|
+
| `routeName` | `string` | `""` | Target route name |
|
|
276
|
+
| `routeParams` | `Params` | `{}` | Route parameters |
|
|
277
|
+
| `routeOptions` | `NavigationOptions` | `{}` | Navigation options (replace, etc.) |
|
|
278
|
+
| `activeClassName` | `string` | `"active"` | CSS class applied when route is active |
|
|
279
|
+
| `activeStrict` | `boolean` | `false` | Exact match only (no ancestor match) |
|
|
280
|
+
| `ignoreQueryParams` | `boolean` | `true` | Query params don't affect active state |
|
|
281
|
+
|
|
282
|
+
### `[realLinkActive]`
|
|
283
|
+
|
|
284
|
+
Applies an active CSS class to any element when a route is active. Use this when you need active state on a non-`<a>` element, or when the clickable element and the styled element are different.
|
|
285
|
+
|
|
286
|
+
```html
|
|
287
|
+
<li [realLinkActive]="'active'" routeName="users" [routeParams]="{}">
|
|
288
|
+
<a realLink routeName="users">Users</a>
|
|
289
|
+
</li>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
| Input | Type | Default | Description |
|
|
293
|
+
| ------------------- | --------- | ------- | -------------------------------------- |
|
|
294
|
+
| `realLinkActive` | `string` | `""` | CSS class to apply when active |
|
|
295
|
+
| `routeName` | `string` | `""` | Route to watch |
|
|
296
|
+
| `routeParams` | `Params` | `{}` | Route parameters |
|
|
297
|
+
| `activeStrict` | `boolean` | `false` | Exact match only |
|
|
298
|
+
| `ignoreQueryParams` | `boolean` | `true` | Query params don't affect active state |
|
|
299
|
+
|
|
300
|
+
### `routeMatch`
|
|
301
|
+
|
|
302
|
+
Structural directive used inside `<route-view>`. Marks an `ng-template` as the content to render when a route segment matches.
|
|
303
|
+
|
|
304
|
+
```html
|
|
305
|
+
<ng-template routeMatch="home">
|
|
306
|
+
<app-home />
|
|
307
|
+
</ng-template>
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### `routeNotFound`
|
|
311
|
+
|
|
312
|
+
Structural directive used inside `<route-view>`. Marks an `ng-template` as the fallback when no segment matches and the route is `UNKNOWN_ROUTE`.
|
|
313
|
+
|
|
314
|
+
```html
|
|
315
|
+
<ng-template routeNotFound>
|
|
316
|
+
<app-not-found />
|
|
317
|
+
</ng-template>
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
## Accessibility
|
|
321
|
+
|
|
322
|
+
Add `<navigation-announcer>` once near the root of your application to enable WCAG-compliant screen reader announcements on every route change:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
import { NavigationAnnouncer } from "@real-router/angular";
|
|
326
|
+
|
|
327
|
+
@Component({
|
|
328
|
+
selector: "app-root",
|
|
329
|
+
imports: [NavigationAnnouncer],
|
|
330
|
+
template: `
|
|
331
|
+
<navigation-announcer />
|
|
332
|
+
<!-- rest of your app -->
|
|
333
|
+
`,
|
|
334
|
+
})
|
|
335
|
+
export class AppComponent {}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
The announcer creates a visually hidden `aria-live` region and announces each navigation to screen readers. See the [Accessibility guide](https://github.com/greydragon888/real-router/wiki/Accessibility) for details.
|
|
339
|
+
|
|
340
|
+
## Angular-Specific Patterns
|
|
341
|
+
|
|
342
|
+
### Signals, Not Observables
|
|
343
|
+
|
|
344
|
+
`injectRoute()` and `injectRouteNode()` return Angular signals, not RxJS observables. Read them in templates directly or call them in computed/effect:
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
@Component({
|
|
348
|
+
template: `
|
|
349
|
+
@if (route.routeState().route; as r) {
|
|
350
|
+
<h1>{{ r.name }}</h1>
|
|
351
|
+
}
|
|
352
|
+
`,
|
|
353
|
+
})
|
|
354
|
+
export class PageComponent {
|
|
355
|
+
readonly route = injectRoute();
|
|
356
|
+
}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
To react to changes in class code, use `effect`:
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
import { effect } from "@angular/core";
|
|
363
|
+
|
|
364
|
+
export class PageComponent {
|
|
365
|
+
readonly route = injectRouteNode("users");
|
|
366
|
+
|
|
367
|
+
constructor() {
|
|
368
|
+
effect(() => {
|
|
369
|
+
const r = this.route.routeState().route;
|
|
370
|
+
if (r) {
|
|
371
|
+
document.title = `Users — ${r.params["id"] ?? "list"}`;
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Injection Context
|
|
379
|
+
|
|
380
|
+
All `inject*` functions must be called within an injection context. The constructor and field initializers are both valid:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// Field initializer — preferred
|
|
384
|
+
export class MyComponent {
|
|
385
|
+
readonly route = injectRoute(); // valid
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Constructor — also valid
|
|
389
|
+
export class MyComponent {
|
|
390
|
+
readonly route: RouteSignals;
|
|
391
|
+
|
|
392
|
+
constructor() {
|
|
393
|
+
this.route = injectRoute(); // valid
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Outside injection context — throws
|
|
398
|
+
export class MyComponent {
|
|
399
|
+
ngOnInit() {
|
|
400
|
+
const route = injectRoute(); // ERROR: not in injection context
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
`sourceToSignal` follows the same rule — it calls `inject(DestroyRef)` internally.
|
|
406
|
+
|
|
407
|
+
### DestroyRef for Cleanup
|
|
408
|
+
|
|
409
|
+
Subscriptions created by `sourceToSignal` and the directives clean up automatically via `DestroyRef.onDestroy`. No manual unsubscribe needed.
|
|
410
|
+
|
|
411
|
+
### Zoneless Compatibility
|
|
412
|
+
|
|
413
|
+
The adapter is signal-first and does not depend on Zone.js. It works with `provideExperimentalZonelessChangeDetection()` out of the box.
|
|
414
|
+
|
|
415
|
+
### ngOnInit for Input-Dependent Setup
|
|
416
|
+
|
|
417
|
+
`RealLink`, `RealLinkActive`, and `RouteView` create their subscription sources in `ngOnInit`, not the constructor. Signal inputs (`input()`) are not available during construction, so setup that reads inputs must be deferred to `ngOnInit`.
|
|
418
|
+
|
|
419
|
+
## Signal Bridge
|
|
420
|
+
|
|
421
|
+
### `sourceToSignal(source)`
|
|
422
|
+
|
|
423
|
+
Bridges any `RouterSource<T>` (from `@real-router/sources`) into an Angular `Signal<T>`. Cleanup wires through `inject(DestroyRef)` — must be called in an injection context. Used internally by `RouterErrorBoundary`; exposed for custom composables that need to bridge router sources into reactive signals.
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
import { sourceToSignal } from "@real-router/angular";
|
|
427
|
+
import { createTransitionSource } from "@real-router/sources";
|
|
428
|
+
|
|
429
|
+
const transitionSignal = sourceToSignal(createTransitionSource(router));
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
## Documentation
|
|
433
|
+
|
|
434
|
+
Full documentation: [Wiki](https://github.com/greydragon888/real-router/wiki)
|
|
435
|
+
|
|
436
|
+
- [RouterProvider](https://github.com/greydragon888/real-router/wiki/RouterProvider) · [RouteView](https://github.com/greydragon888/real-router/wiki/RouteView) · [RouterErrorBoundary](https://github.com/greydragon888/real-router/wiki/RouterErrorBoundary)
|
|
437
|
+
- [injectRouter](https://github.com/greydragon888/real-router/wiki/injectRouter) · [injectRoute](https://github.com/greydragon888/real-router/wiki/injectRoute) · [injectRouteNode](https://github.com/greydragon888/real-router/wiki/injectRouteNode) · [injectNavigator](https://github.com/greydragon888/real-router/wiki/injectNavigator) · [injectRouteUtils](https://github.com/greydragon888/real-router/wiki/injectRouteUtils) · [injectRouterTransition](https://github.com/greydragon888/real-router/wiki/injectRouterTransition)
|
|
438
|
+
|
|
439
|
+
## Related Packages
|
|
440
|
+
|
|
441
|
+
| Package | Description |
|
|
442
|
+
| ---------------------------------------------------------------------------------------- | --------------------------------------- |
|
|
443
|
+
| [@real-router/core](https://www.npmjs.com/package/@real-router/core) | Core router (required dependency) |
|
|
444
|
+
| [@real-router/browser-plugin](https://www.npmjs.com/package/@real-router/browser-plugin) | Browser History API integration |
|
|
445
|
+
| [@real-router/sources](https://www.npmjs.com/package/@real-router/sources) | Subscription layer (used internally) |
|
|
446
|
+
| [@real-router/route-utils](https://www.npmjs.com/package/@real-router/route-utils) | Route tree queries (`injectRouteUtils`) |
|
|
447
|
+
|
|
448
|
+
## Contributing
|
|
449
|
+
|
|
450
|
+
See [contributing guidelines](../../CONTRIBUTING.md) for development setup and PR process.
|
|
451
|
+
|
|
452
|
+
## License
|
|
453
|
+
|
|
454
|
+
[MIT](../../LICENSE) © [Oleg Ivanov](https://github.com/greydragon888)
|