UNPKG

4.85 kBMarkdownView Raw
1# Abstract Nested Router
2
3> It _tries_ to capture all matching routes from its **root**.
4>
5> [![Build Status](https://api.travis-ci.org/pateketrueke/abstract-nested-router.svg?branch=master)](https://travis-ci.org/pateketrueke/abstract-nested-router)
6> [![NPM version](https://badge.fury.io/js/abstract-nested-router.svg)](http://badge.fury.io/js/abstract-nested-router)
7> [![Coverage Status](https://codecov.io/github/pateketrueke/abstract-nested-router/coverage.svg?branch=master)](https://codecov.io/github/pateketrueke/abstract-nested-router)
8> [![Known Vulnerabilities](https://snyk.io/test/npm/abstract-nested-router/badge.svg)](https://snyk.io/test/npm/abstract-nested-router)
9
10```js
11import Router from 'abstract-nested-router';
12
13const r = new Router();
14
15// single routes
16r.add('/', { is: 'home' });
17r.add('/*_', { is: 'catch' });
18
19// nested routes
20r.mount('/:a', () => {
21 r.add('/*_', { is: 'undef' });
22 r.add('/:b/:c', { is: 'nested' });
23});
24
25r.find('/');
26[ { is: 'home', params: {}, route: '/', path: '/' } ]
27
28r.find('/test');
29[ { is: 'home', params: {}, route: '/', path: '/' },
30 { is: 'undef',
31 params: { a: 'test' },
32 route: '/:a',
33 path: '/test' } ]
34
35r.find('/x/y');
36[ { is: 'home', params: {}, route: '/', path: '/' },
37 { is: 'undef', params: { a: 'x' }, route: '/:a', path: '/x' },
38 { is: 'nested',
39 params: { a: 'x', b: 'y' },
40 route: '/:a/:b',
41 path: '/x/y' } ]
42
43r.find('/x/y/z');
44[ { is: 'home', params: {}, route: '/', path: '/' },
45 { is: 'undef', params: { a: 'x' }, route: '/:a', path: '/x' },
46 { is: 'nested',
47 params: { a: 'x', b: 'y' },
48 route: '/:a/:b',
49 path: '/x/y' },
50 { is: 'nested',
51 params: { a: 'x', b: 'y', c: 'z' },
52 route: '/:a/:b/:c',
53 path: '/x/y/z' } ]
54
55r.find('/x/y/z/0');
56// Error: Unreachable '/x/y/z/0', segment '/0' is not defined
57
58r.find('/x/y/z/0', true);
59[ { is: 'home', params: {}, route: '/', path: '/' },
60 { is: 'catch',
61 params: { _: 'x/y/z/0' },
62 route: '/*_',
63 path: '/x/y/z/0' } ]
64```
65
66In the latter example `catch` is resolved just after the previous failure of `/x/y/z/0` because we're trying at least twice.
67
68## API
69
70Available methods:
71
72- `resolve(path, cb)` — Progressively finds and invoke callback with `(err, routes)` as input, useful for third-party integrations, e.g. [yrv](https://www.npmjs.com/package/yrv)
73- `mount(path, cb)` — Allow to register routes under the same route
74- `find(path[, retries])` — Look up routes by path, in case of failure try passing `retries` as true
75- `add(path[, routeInfo])` — Register a single route by path, additional info will be returned on match
76- `rm(path)` — Remove a single route by full-path, it will fail if given route is not registered!
77
78### Params
79
80By default all segments are optional, e.g. `/a/:b/:c` matches with `/a`, `/a/x` and `/a/x/y` so you can say `:b` and `:c` are optional parameters.
81
82More advanced cases would require fragments to be optional, e.g. `/:foo(-bar)` matches with `/x` and `/x-bar` because `-bar` is an optional fragment.
83
84In the latter case `params.foo` will always be `x` regardless if `-bar` is appended, if you want to match `bar` then use `/:foo(-:suffix)` instead.
85
86> _Splat_ parameters will consume the rest of the segments/fragments if they're present, e.g. `/x*y` captures anything that begins with `x` and stores it on `params.y` so it matches `/xy`, `/xabc`, `/x/y`, `/x/a/b/c` and so on.
87
88Every parameter can hold simple regex-like patterns, e.g. `/:id<\d+>`
89
90Supported patterns:
91
92- `/:x` and `/*y` are optional segments and they cannot be empty
93- `<...>` to hold regex-like patterns, `-$.` are escaped, `/` is forbidden
94- `(...)` are used to mark fragments as optional, it translates to `(?:...)?`
95
96> Please avoid `/` inside `(...)` or `<...>` as they will fail loudly!
97
98### Nesting
99
100Consider the following examples:
101
102```js
103// 1. regular
104r.add('/a');
105r.add('/a/:b');
106r.add('/a/:b/:c');
107
108// 2. nested
109r.mount('/a', () => {
110 r.mount('/:b', () => {
111 r.add('/:c');
112 });
113});
114
115// 3. concise
116r.add('/a/:b/:c');
117```
118
119In the former way (1) we're declaring each route-level by hand, however they can be expressed at once as that latter one (3) which is more concise.
120
121The middle form (2) is a shortcut to produce concise routes.
122
123So which one is the best? It depends on the context:
124
125- Use concise routes to share the same `routeInfo` on all segments, it will be applied only if it's not yet defined on the route.
126- Use nested routes to use shared paths, it's convenient for creating stacks of context while mounting routes, etc.
127- Use regular routes to gain full control over its definition, this way each route can have its own separated context.
128
129> Routes are sorted and matched by priority and type, routes with splat params will be tried last. As more static and with less parameters the route will be matched sooner!