@ng-native/router 0.5.0 → 0.7.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 +10 -0
- package/dist/file-routes.d.ts +83 -0
- package/dist/file-routes.js +371 -0
- package/dist/file-routes.js.map +1 -0
- package/dist/in-tab.d.ts +20 -0
- package/dist/in-tab.js +13 -0
- package/dist/in-tab.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/markdown-page.d.ts +39 -0
- package/dist/markdown-page.js +56 -0
- package/dist/markdown-page.js.map +1 -0
- package/dist/native-back.d.ts +8 -0
- package/dist/native-back.js +8 -0
- package/dist/native-back.js.map +1 -1
- package/dist/native-navigation.d.ts +11 -1
- package/dist/native-navigation.js +66 -2
- package/dist/native-navigation.js.map +1 -1
- package/dist/native-platform-location.d.ts +5 -0
- package/dist/native-platform-location.js +4 -0
- package/dist/native-platform-location.js.map +1 -1
- package/dist/native-router-link.js +8 -6
- package/dist/native-router-link.js.map +1 -1
- package/dist/native-stack-outlet.d.ts +6 -0
- package/dist/native-stack-outlet.js +25 -2
- package/dist/native-stack-outlet.js.map +1 -1
- package/dist/native-stack-reuse-strategy.d.ts +9 -0
- package/dist/native-stack-reuse-strategy.js +28 -8
- package/dist/native-stack-reuse-strategy.js.map +1 -1
- package/dist/native-tab.d.ts +6 -0
- package/dist/native-tab.js +11 -2
- package/dist/native-tab.js.map +1 -1
- package/dist/native-tabs-outlet.d.ts +29 -1
- package/dist/native-tabs-outlet.js +113 -28
- package/dist/native-tabs-outlet.js.map +1 -1
- package/dist/native-toolbar.d.ts +39 -0
- package/dist/native-toolbar.js +108 -0
- package/dist/native-toolbar.js.map +1 -0
- package/dist/presented-route.d.ts +9 -1
- package/dist/presented-route.js +1 -1
- package/dist/presented-route.js.map +1 -1
- package/dist/provide-native-router.js +86 -2
- package/dist/provide-native-router.js.map +1 -1
- package/dist/route-parking.d.ts +30 -0
- package/dist/route-parking.js +60 -0
- package/dist/route-parking.js.map +1 -0
- package/dist/screens.js +23 -0
- package/dist/screens.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -65,10 +65,20 @@ export class App {
|
|
|
65
65
|
- `nativeRouterLink` - the native equivalent of `routerLink`.
|
|
66
66
|
- `NativeNavigation` - replacing a screen, presenting a modal or sheet, resetting the stack: what a
|
|
67
67
|
URL alone cannot express.
|
|
68
|
+
- `fileRoutes(pages)` - routes made from the files in `src/app/pages`, with Analog's file names
|
|
69
|
+
(`index.page.ts`, `[id].page.ts`, `[...slug].page.ts`, `(group)` folders, layouts and `.md`
|
|
70
|
+
pages), found by Metro's `require.context`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
const pages = require.context('./pages', true, /\.page\.ts$/, 'lazy');
|
|
74
|
+
|
|
75
|
+
provideNativeRouter(fileRoutes(pages), withComponentInputBinding());
|
|
76
|
+
```
|
|
68
77
|
|
|
69
78
|
## Docs
|
|
70
79
|
|
|
71
80
|
- [Router](https://ng-native.com/packages/router)
|
|
81
|
+
- [File routes](https://ng-native.com/packages/router/file-routes)
|
|
72
82
|
- [Root README](https://github.com/ng-native/ng-native/blob/main/README.md) and
|
|
73
83
|
[ARCHITECTURE.md](https://github.com/ng-native/ng-native/blob/main/docs/ARCHITECTURE.md)
|
|
74
84
|
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based routing: each page is a file in `src/app/pages`, and its path there is its URL.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* const pages = require.context('./pages', true, /\.page\.ts$/, 'lazy');
|
|
6
|
+
*
|
|
7
|
+
* mount(rootTag, App, fabric, { providers: [provideNativeRouter(fileRoutes(pages))] });
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* The file names are Analog's (`index.page.ts`, `[id].page.ts`, `[...slug].page.ts`, `(group)`
|
|
11
|
+
* folders, `products.page.ts` beside `products/` as a layout) and so is a page module's shape (a
|
|
12
|
+
* default export and an optional `routeMeta`), so a page moves between an app routed by
|
|
13
|
+
* `fileRoutes` and one routed by `@ng-native/analog` without being renamed. Nothing here imports
|
|
14
|
+
* Analog: the routes are made here, as plain `@angular/router` routes.
|
|
15
|
+
*/
|
|
16
|
+
import { type Type } from '@angular/core';
|
|
17
|
+
import { type CanMatchFn, type Route, type Routes } from '@angular/router';
|
|
18
|
+
/**
|
|
19
|
+
* What Metro's `require.context(directory, true, pattern, mode)` returns: the files it found, each
|
|
20
|
+
* keyed by its path from that directory (`./users/[id].page.ts`), and a call that loads one, which
|
|
21
|
+
* returns a promise in `'lazy'` mode and the module itself in `'sync'` mode.
|
|
22
|
+
*/
|
|
23
|
+
export interface PageContext {
|
|
24
|
+
keys(): string[];
|
|
25
|
+
(key: string): unknown;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The files of an `import.meta.glob`, as Vite and Vitest make it: each file's path, with a call
|
|
29
|
+
* that loads it (`import.meta.glob('./pages/**\/*.page.ts')`) or the module itself
|
|
30
|
+
* (`{ eager: true }`).
|
|
31
|
+
*/
|
|
32
|
+
export type PageFiles = Readonly<Record<string, unknown>>;
|
|
33
|
+
export interface FileRoutesOptions {
|
|
34
|
+
/**
|
|
35
|
+
* The component a `.md` page is drawn with. It reads its file with `injectMarkdownPage()` and
|
|
36
|
+
* draws `tokens` with `<markdown>` from `@ng-native/components/markdown`. Required when the
|
|
37
|
+
* pages include a `.md` file.
|
|
38
|
+
*/
|
|
39
|
+
markdownPage?: Type<unknown>;
|
|
40
|
+
}
|
|
41
|
+
/** What a page's file sets about its route by being where it is, so `routeMeta` does not. */
|
|
42
|
+
declare const SET_BY_THE_FILE: readonly ["path", "matcher", "component", "loadComponent", "children", "loadChildren", "canLoad", "outlet"];
|
|
43
|
+
/**
|
|
44
|
+
* A page's `export const routeMeta`, as Analog has it: the page's route config, less what the
|
|
45
|
+
* file's place sets. `meta`, the page's HTML meta tags in Analog, is accepted and left out, since
|
|
46
|
+
* there is no document head on a device. A page that only redirects has `redirectTo` and no
|
|
47
|
+
* component; its `pathMatch` is `'full'` unless it says otherwise.
|
|
48
|
+
*/
|
|
49
|
+
export type RouteMeta = (Omit<Route, (typeof SET_BY_THE_FILE)[number] | 'redirectTo' | 'pathMatch' | 'canMatch'> & {
|
|
50
|
+
canMatch?: CanMatchFn[];
|
|
51
|
+
meta?: unknown;
|
|
52
|
+
redirectTo?: never;
|
|
53
|
+
}) | {
|
|
54
|
+
redirectTo: NonNullable<Route['redirectTo']>;
|
|
55
|
+
pathMatch?: Route['pathMatch'];
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* The routes for an app's pages, for `provideNativeRouter`.
|
|
59
|
+
*
|
|
60
|
+
* `pages` is the `require.context` of the pages folder, or, in a Vitest test, which has no
|
|
61
|
+
* `require.context`, the files of an `import.meta.glob` such as
|
|
62
|
+
* `import.meta.glob('./pages/**\/*.page.ts')`. A page from a `require.context`, or from a glob
|
|
63
|
+
* that loads each file, is loaded the first time it is navigated to; a page from an eager glob is
|
|
64
|
+
* routed as it is.
|
|
65
|
+
*
|
|
66
|
+
* | File | Route |
|
|
67
|
+
* | --------------------- | ---------------------------------------------------------- |
|
|
68
|
+
* | `index.page.ts` | `''`, the folder's own page |
|
|
69
|
+
* | `about.page.ts` | `about` |
|
|
70
|
+
* | `[id].page.ts` | `:id` |
|
|
71
|
+
* | `[...slug].page.ts` | all under its folder, the rest of the URL as `slug` |
|
|
72
|
+
* | `(auth)/` | no segment of its own |
|
|
73
|
+
* | `blog.[slug].page.ts` | `blog/:slug`, without a layout |
|
|
74
|
+
* | `products.page.ts` | a layout around `products/`, with an outlet for its pages |
|
|
75
|
+
*
|
|
76
|
+
* At each level a static path comes first, then a group's layout, then a parameter, then a
|
|
77
|
+
* catch-all. Two files at the same URL, a name with brackets that are not one of the above, and a
|
|
78
|
+
* `.md` page with no `options.markdownPage` each throw here, naming the file. A page with no
|
|
79
|
+
* default export throws here when it is eager, and on the navigation that loads it when it is
|
|
80
|
+
* lazy.
|
|
81
|
+
*/
|
|
82
|
+
export declare function fileRoutes(pages: PageContext | PageFiles, options?: FileRoutesOptions): Routes;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based routing: each page is a file in `src/app/pages`, and its path there is its URL.
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* const pages = require.context('./pages', true, /\.page\.ts$/, 'lazy');
|
|
6
|
+
*
|
|
7
|
+
* mount(rootTag, App, fabric, { providers: [provideNativeRouter(fileRoutes(pages))] });
|
|
8
|
+
* ```
|
|
9
|
+
*
|
|
10
|
+
* The file names are Analog's (`index.page.ts`, `[id].page.ts`, `[...slug].page.ts`, `(group)`
|
|
11
|
+
* folders, `products.page.ts` beside `products/` as a layout) and so is a page module's shape (a
|
|
12
|
+
* default export and an optional `routeMeta`), so a page moves between an app routed by
|
|
13
|
+
* `fileRoutes` and one routed by `@ng-native/analog` without being renamed. Nothing here imports
|
|
14
|
+
* Analog: the routes are made here, as plain `@angular/router` routes.
|
|
15
|
+
*/
|
|
16
|
+
import { EnvironmentInjector, inject, runInInjectionContext } from '@angular/core';
|
|
17
|
+
import { UrlSegment, } from '@angular/router';
|
|
18
|
+
import { MARKDOWN_PAGE, markdownPageFile } from "./markdown-page.js";
|
|
19
|
+
/** What a page's file sets about its route by being where it is, so `routeMeta` does not. */
|
|
20
|
+
const SET_BY_THE_FILE = [
|
|
21
|
+
'path',
|
|
22
|
+
'matcher',
|
|
23
|
+
'component',
|
|
24
|
+
'loadComponent',
|
|
25
|
+
'children',
|
|
26
|
+
'loadChildren',
|
|
27
|
+
'canLoad',
|
|
28
|
+
'outlet',
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* The routes for an app's pages, for `provideNativeRouter`.
|
|
32
|
+
*
|
|
33
|
+
* `pages` is the `require.context` of the pages folder, or, in a Vitest test, which has no
|
|
34
|
+
* `require.context`, the files of an `import.meta.glob` such as
|
|
35
|
+
* `import.meta.glob('./pages/**\/*.page.ts')`. A page from a `require.context`, or from a glob
|
|
36
|
+
* that loads each file, is loaded the first time it is navigated to; a page from an eager glob is
|
|
37
|
+
* routed as it is.
|
|
38
|
+
*
|
|
39
|
+
* | File | Route |
|
|
40
|
+
* | --------------------- | ---------------------------------------------------------- |
|
|
41
|
+
* | `index.page.ts` | `''`, the folder's own page |
|
|
42
|
+
* | `about.page.ts` | `about` |
|
|
43
|
+
* | `[id].page.ts` | `:id` |
|
|
44
|
+
* | `[...slug].page.ts` | all under its folder, the rest of the URL as `slug` |
|
|
45
|
+
* | `(auth)/` | no segment of its own |
|
|
46
|
+
* | `blog.[slug].page.ts` | `blog/:slug`, without a layout |
|
|
47
|
+
* | `products.page.ts` | a layout around `products/`, with an outlet for its pages |
|
|
48
|
+
*
|
|
49
|
+
* At each level a static path comes first, then a group's layout, then a parameter, then a
|
|
50
|
+
* catch-all. Two files at the same URL, a name with brackets that are not one of the above, and a
|
|
51
|
+
* `.md` page with no `options.markdownPage` each throw here, naming the file. A page with no
|
|
52
|
+
* default export throws here when it is eager, and on the navigation that loads it when it is
|
|
53
|
+
* lazy.
|
|
54
|
+
*/
|
|
55
|
+
export function fileRoutes(pages, options = {}) {
|
|
56
|
+
const found = typeof pages === 'function' ? fromContext(pages) : fromGlob(pages);
|
|
57
|
+
const root = { parts: [], pages: [], children: new Map() };
|
|
58
|
+
for (const [path, page] of found) {
|
|
59
|
+
if (page.markdown && !options.markdownPage) {
|
|
60
|
+
throw new Error(`${page.key} is a Markdown page, and fileRoutes has no component to draw it with. Pass ` +
|
|
61
|
+
'one: fileRoutes(pages, { markdownPage }). See injectMarkdownPage.');
|
|
62
|
+
}
|
|
63
|
+
const names = path.split('/');
|
|
64
|
+
let entry = root;
|
|
65
|
+
for (const name of names) {
|
|
66
|
+
let next = entry.children.get(name);
|
|
67
|
+
if (!next) {
|
|
68
|
+
next = { parts: partsOf(name, page.key), pages: [], children: new Map() };
|
|
69
|
+
entry.children.set(name, next);
|
|
70
|
+
}
|
|
71
|
+
entry = next;
|
|
72
|
+
}
|
|
73
|
+
entry.pages.push(page);
|
|
74
|
+
}
|
|
75
|
+
checkPaths(root, [], new Map());
|
|
76
|
+
return routesOf(root, options).map((ranked) => ranked.route);
|
|
77
|
+
}
|
|
78
|
+
/** The context's files, each by its path from the pages folder without its extension. */
|
|
79
|
+
function fromContext(pages) {
|
|
80
|
+
return pages.keys().map((key) => {
|
|
81
|
+
const path = key.replace(/\\/g, '/').replace(/^\.?\//, '');
|
|
82
|
+
return named(key, path, () => pages(key), true);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The glob's files, each by its path from the pages folder: what follows the first `pages/` in
|
|
87
|
+
* its key (`/src/app/pages/about.page.ts`, `./pages/about.page.ts`), or the key itself when it
|
|
88
|
+
* has none, for a glob made inside the pages folder.
|
|
89
|
+
*/
|
|
90
|
+
function fromGlob(pages) {
|
|
91
|
+
return Object.keys(pages).map((key) => {
|
|
92
|
+
const slashed = key.replace(/\\/g, '/');
|
|
93
|
+
const path = /^(?:.*?\/)?pages\/(.*)$/.exec(slashed)?.[1] ?? slashed.replace(/^\.?\//, '');
|
|
94
|
+
const value = pages[key];
|
|
95
|
+
return typeof value === 'function'
|
|
96
|
+
? named(key, path, value, true)
|
|
97
|
+
: named(key, path, () => value, false);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
function named(key, path, load, lazy) {
|
|
101
|
+
const markdown = path.endsWith('.md');
|
|
102
|
+
const stem = path.replace(/\.page\.[cm]?[jt]s$/, '').replace(/\.md$/, '');
|
|
103
|
+
if (stem === path) {
|
|
104
|
+
throw new Error(`${key} is not a page. A page's file name ends in .page.ts, or .md for a Markdown page: ` +
|
|
105
|
+
'narrow the require.context or import.meta.glob to those.');
|
|
106
|
+
}
|
|
107
|
+
return [stem, { key, file: path, markdown, load, lazy }];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* The URL parts a file or folder name makes. A dot splits a name into segments, as in Analog
|
|
111
|
+
* (`blog.[slug]` is `blog/:slug`), except inside brackets (`[...slug]`).
|
|
112
|
+
*/
|
|
113
|
+
function partsOf(name, key) {
|
|
114
|
+
return name.split(/\.(?![^[]*\])/).map((piece) => {
|
|
115
|
+
if (piece === 'index')
|
|
116
|
+
return { kind: 'static', path: '' };
|
|
117
|
+
if (/^\([^()[\]./]+\)$/.test(piece))
|
|
118
|
+
return { kind: 'group' };
|
|
119
|
+
const rest = /^\[\[\.\.\.([\w$-]+)\]\]$/.exec(piece) ?? /^\[\.\.\.([\w$-]+)\]$/.exec(piece);
|
|
120
|
+
if (rest)
|
|
121
|
+
return { kind: 'rest', name: rest[1] };
|
|
122
|
+
const param = /^\[([\w$-]+)\]$/.exec(piece);
|
|
123
|
+
if (param)
|
|
124
|
+
return { kind: 'param', name: param[1] };
|
|
125
|
+
if (piece && !/[[\]():]/.test(piece))
|
|
126
|
+
return { kind: 'static', path: piece };
|
|
127
|
+
throw new Error(`${key}: "${piece}" is not a page name fileRoutes reads. A name is a segment (about), a ` +
|
|
128
|
+
'parameter ([id]), a catch-all ([...slug]) or a group ((auth)), and index is the ' +
|
|
129
|
+
"folder's own page.");
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Throws for a catch-all with anything after it, and for two pages at one URL: two files for one
|
|
134
|
+
* name (`about.page.ts` and `about.md`), or two pages whose paths differ only in groups or in
|
|
135
|
+
* parameter names. A layout is not a page at its URL; its folder's `index.page.ts` is.
|
|
136
|
+
*/
|
|
137
|
+
function checkPaths(entry, above, seen) {
|
|
138
|
+
for (const child of entry.children.values()) {
|
|
139
|
+
const parts = [...above, ...child.parts];
|
|
140
|
+
const rest = parts.findIndex((part) => part.kind === 'rest');
|
|
141
|
+
const key = child.pages[0]?.key ?? firstKey(child);
|
|
142
|
+
if (rest !== -1 && (rest < parts.length - 1 || child.children.size > 0)) {
|
|
143
|
+
throw new Error(`${key}: a catch-all ([...slug]) takes the rest of the URL, so it is the last part of a ` +
|
|
144
|
+
'path, with no folder of pages under it.');
|
|
145
|
+
}
|
|
146
|
+
if (child.pages.length > 1) {
|
|
147
|
+
throw new Error(`${child.pages[0].key} and ${child.pages[1].key} are the same page. Keep one of them.`);
|
|
148
|
+
}
|
|
149
|
+
if (child.pages.length === 1 && child.children.size === 0) {
|
|
150
|
+
const url = urlOf(parts);
|
|
151
|
+
const other = seen.get(url);
|
|
152
|
+
if (other) {
|
|
153
|
+
throw new Error(`${other} and ${key} are both the page at /${url}. Keep one of them, or move one.`);
|
|
154
|
+
}
|
|
155
|
+
seen.set(url, key);
|
|
156
|
+
}
|
|
157
|
+
checkPaths(child, parts, seen);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function firstKey(entry) {
|
|
161
|
+
for (const child of entry.children.values())
|
|
162
|
+
return child.pages[0]?.key ?? firstKey(child);
|
|
163
|
+
return '';
|
|
164
|
+
}
|
|
165
|
+
/** A page's URL as a pattern, with every parameter alike, for telling two pages at one URL. */
|
|
166
|
+
function urlOf(parts) {
|
|
167
|
+
return parts
|
|
168
|
+
.flatMap((part) => {
|
|
169
|
+
if (part.kind === 'param')
|
|
170
|
+
return [':'];
|
|
171
|
+
if (part.kind === 'rest')
|
|
172
|
+
return ['**'];
|
|
173
|
+
return part.kind === 'static' && part.path ? [part.path] : [];
|
|
174
|
+
})
|
|
175
|
+
.join('/');
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The routes under `entry`, in the order Angular should try them: a static path first, a longer
|
|
179
|
+
* one before a shorter it starts with, then a group's layout, then a path with a parameter, then a
|
|
180
|
+
* catch-all, each by its path. A group with no layout of its own adds nothing to the URL or the
|
|
181
|
+
* tree, so its routes join its siblings'.
|
|
182
|
+
*/
|
|
183
|
+
function routesOf(entry, options) {
|
|
184
|
+
const ranked = [];
|
|
185
|
+
for (const child of entry.children.values()) {
|
|
186
|
+
const children = routesOf(child, options);
|
|
187
|
+
const path = pathOf(child.parts);
|
|
188
|
+
if (!child.pages[0] && !path)
|
|
189
|
+
ranked.push(...children);
|
|
190
|
+
else
|
|
191
|
+
ranked.push(rankedRoute(child, path, children, options));
|
|
192
|
+
}
|
|
193
|
+
return ranked.sort((a, b) => a.rank - b.rank || byPath(a.path, b.path) || (a.key < b.key ? -1 : 1));
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Paths by their segments, a longer one before the path it starts with: `users/new` is tried
|
|
197
|
+
* before the `users` folder, whose `:id` would take `new`, as Analog orders them.
|
|
198
|
+
*/
|
|
199
|
+
function byPath(a, b) {
|
|
200
|
+
const left = a.split('/');
|
|
201
|
+
const right = b.split('/');
|
|
202
|
+
for (let i = 0; i < Math.min(left.length, right.length); i++) {
|
|
203
|
+
if (left[i] !== right[i])
|
|
204
|
+
return left[i] < right[i] ? -1 : 1;
|
|
205
|
+
}
|
|
206
|
+
return right.length - left.length;
|
|
207
|
+
}
|
|
208
|
+
/** The route for a file or folder name at `path`, with its routes under it. */
|
|
209
|
+
function rankedRoute(entry, path, children, options) {
|
|
210
|
+
const page = entry.pages[0];
|
|
211
|
+
const rest = entry.parts.find((part) => part.kind === 'rest');
|
|
212
|
+
const nested = children.length ? children.map((child) => child.route) : undefined;
|
|
213
|
+
let route = page
|
|
214
|
+
? pageRoute(page, rest ? { matcher: restOf(rest.name) } : { path }, nested, options)
|
|
215
|
+
: { path, children: nested };
|
|
216
|
+
if (rest && path)
|
|
217
|
+
route = { path, children: [route] };
|
|
218
|
+
const rank = rest ? 3 : path.includes(':') ? 2 : !path && nested ? 1 : 0;
|
|
219
|
+
return { rank, path, key: page?.key ?? firstKey(entry), route };
|
|
220
|
+
}
|
|
221
|
+
/** The path a name's parts make, without its groups, its index or its catch-all. */
|
|
222
|
+
function pathOf(parts) {
|
|
223
|
+
return parts
|
|
224
|
+
.flatMap((part) => (part.kind === 'static' || part.kind === 'param' ? [segmentOf(part)] : []))
|
|
225
|
+
.filter(Boolean)
|
|
226
|
+
.join('/');
|
|
227
|
+
}
|
|
228
|
+
function segmentOf(part) {
|
|
229
|
+
if (part.kind === 'param')
|
|
230
|
+
return `:${part.name}`;
|
|
231
|
+
return part.kind === 'static' ? part.path : '';
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* A catch-all's matcher: it takes every segment left, none included, as Analog's `**` does, and
|
|
235
|
+
* hands them over as one parameter, `docs/intro` for `/docs/intro` under the catch-all's folder.
|
|
236
|
+
*/
|
|
237
|
+
function restOf(name) {
|
|
238
|
+
return (segments) => ({
|
|
239
|
+
consumed: segments,
|
|
240
|
+
posParams: { [name]: new UrlSegment(segments.map((segment) => segment.path).join('/'), {}) },
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* A page's route, at `at`: one with no component that takes the page's place in the URL, and the
|
|
245
|
+
* page's own route under it at `''`, as Analog makes it, so the page's `routeMeta` can wait for a
|
|
246
|
+
* lazy page to load. The page's `children`, when it is a layout, go under its own route.
|
|
247
|
+
*
|
|
248
|
+
* `canMatch` is on the outer route, not the page's own: Angular takes a route whose children match
|
|
249
|
+
* none of an empty rest of the URL as matched, so a page refused there would leave an empty screen
|
|
250
|
+
* where the next route that matches should be. An index page's outer route matches only the whole
|
|
251
|
+
* URL, so its guards, and its load when it is lazy, run for no other page's URL: a guard that
|
|
252
|
+
* redirects would otherwise run again for the URL it sent the router to.
|
|
253
|
+
*/
|
|
254
|
+
function pageRoute(page, at, children, options) {
|
|
255
|
+
if (at.path === '' && !children)
|
|
256
|
+
at = { ...at, pathMatch: 'full' };
|
|
257
|
+
if (!page.lazy) {
|
|
258
|
+
const { canMatch, ...own } = ownRoute(page, page.load(), children, options);
|
|
259
|
+
return { ...at, ...(canMatch ? { canMatch } : {}), children: [own] };
|
|
260
|
+
}
|
|
261
|
+
const load = loaderOf(page);
|
|
262
|
+
const loadChildren = async () => {
|
|
263
|
+
const { canMatch: _matchedAbove, ...own } = ownRoute(page, await load(), children, options);
|
|
264
|
+
return [own];
|
|
265
|
+
};
|
|
266
|
+
if (page.markdown)
|
|
267
|
+
return { ...at, loadChildren };
|
|
268
|
+
const canMatch = (...args) => {
|
|
269
|
+
const injector = inject(EnvironmentInjector);
|
|
270
|
+
return load().then((module) => passes(module?.routeMeta, injector, args));
|
|
271
|
+
};
|
|
272
|
+
return { ...at, canMatch: [canMatch], loadChildren };
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Loads a lazy page once for its `canMatch` and its routes both. A load that fails is forgotten,
|
|
276
|
+
* so the next navigation tries again, as Angular does with a `loadChildren` that fails.
|
|
277
|
+
*/
|
|
278
|
+
function loaderOf(page) {
|
|
279
|
+
let loading;
|
|
280
|
+
return () => (loading ??= Promise.resolve()
|
|
281
|
+
.then(page.load)
|
|
282
|
+
.catch((error) => {
|
|
283
|
+
loading = undefined;
|
|
284
|
+
throw error;
|
|
285
|
+
}));
|
|
286
|
+
}
|
|
287
|
+
/** Runs a loaded page's `canMatch` guards in turn, until one says other than `true`. */
|
|
288
|
+
async function passes(meta, injector, args) {
|
|
289
|
+
const guards = meta?.canMatch ?? [];
|
|
290
|
+
for (const guard of guards) {
|
|
291
|
+
const result = await firstOf(runInInjectionContext(injector, () => guard(...args)));
|
|
292
|
+
if (result !== true)
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
/** A guard's result, from an Observable's first value or a promise as from a value. */
|
|
298
|
+
function firstOf(result) {
|
|
299
|
+
const observable = result;
|
|
300
|
+
if (typeof observable?.subscribe !== 'function')
|
|
301
|
+
return Promise.resolve(result);
|
|
302
|
+
const source = observable;
|
|
303
|
+
return new Promise((resolve, reject) => {
|
|
304
|
+
let done = false;
|
|
305
|
+
let subscription;
|
|
306
|
+
subscription = source.subscribe({
|
|
307
|
+
next: (value) => {
|
|
308
|
+
if (done)
|
|
309
|
+
return;
|
|
310
|
+
done = true;
|
|
311
|
+
resolve(value);
|
|
312
|
+
subscription?.unsubscribe();
|
|
313
|
+
},
|
|
314
|
+
error: (error) => {
|
|
315
|
+
if (done)
|
|
316
|
+
return;
|
|
317
|
+
done = true;
|
|
318
|
+
reject(error);
|
|
319
|
+
},
|
|
320
|
+
complete: () => {
|
|
321
|
+
if (done)
|
|
322
|
+
return;
|
|
323
|
+
done = true;
|
|
324
|
+
resolve(false);
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
if (done)
|
|
328
|
+
subscription.unsubscribe();
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
function ownRoute(page, loaded, children, options) {
|
|
332
|
+
const module = page.markdown ? markdownModule(page, loaded, options) : loaded;
|
|
333
|
+
const meta = (module?.routeMeta ?? {});
|
|
334
|
+
const set = SET_BY_THE_FILE.find((name) => name in meta);
|
|
335
|
+
if (set) {
|
|
336
|
+
throw new Error(`${page.key} sets routeMeta.${set}, which a page's file sets by where it is. Leave it out.`);
|
|
337
|
+
}
|
|
338
|
+
const config = { ...meta };
|
|
339
|
+
delete config.meta;
|
|
340
|
+
if (config.redirectTo !== undefined) {
|
|
341
|
+
if (children) {
|
|
342
|
+
throw new Error(`${page.key} redirects, and a page that redirects is not a layout: its folder's pages ` +
|
|
343
|
+
'would never show. Redirect from a page with no folder beside it.');
|
|
344
|
+
}
|
|
345
|
+
return { path: '', pathMatch: 'full', ...config };
|
|
346
|
+
}
|
|
347
|
+
if (typeof module?.default !== 'function') {
|
|
348
|
+
throw new Error(`${page.key} has no default export. A page exports its component as the default: ` +
|
|
349
|
+
'`export default class AboutPage {}`.');
|
|
350
|
+
}
|
|
351
|
+
return {
|
|
352
|
+
path: '',
|
|
353
|
+
component: module.default,
|
|
354
|
+
runGuardsAndResolvers: 'paramsOrQueryParamsChange',
|
|
355
|
+
...config,
|
|
356
|
+
...(children ? { children } : {}),
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
/** A `.md` page as a page module: `markdownPage`, titled by the file's front matter. */
|
|
360
|
+
function markdownModule(page, loaded, options) {
|
|
361
|
+
const file = markdownPageFile(page.file, loaded);
|
|
362
|
+
const title = file.attributes['title'];
|
|
363
|
+
return {
|
|
364
|
+
default: options.markdownPage,
|
|
365
|
+
routeMeta: {
|
|
366
|
+
...(typeof title === 'string' ? { title } : {}),
|
|
367
|
+
data: { [MARKDOWN_PAGE]: file },
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
//# sourceMappingURL=file-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-routes.js","sourceRoot":"","sources":["../src/file-routes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * File-based routing: each page is a file in `src/app/pages`, and its path there is its URL.\n *\n * ```ts\n * const pages = require.context('./pages', true, /\\.page\\.ts$/, 'lazy');\n *\n * mount(rootTag, App, fabric, { providers: [provideNativeRouter(fileRoutes(pages))] });\n * ```\n *\n * The file names are Analog's (`index.page.ts`, `[id].page.ts`, `[...slug].page.ts`, `(group)`\n * folders, `products.page.ts` beside `products/` as a layout) and so is a page module's shape (a\n * default export and an optional `routeMeta`), so a page moves between an app routed by\n * `fileRoutes` and one routed by `@ng-native/analog` without being renamed. Nothing here imports\n * Analog: the routes are made here, as plain `@angular/router` routes.\n */\nimport { EnvironmentInjector, inject, runInInjectionContext, type Type } from '@angular/core';\nimport {\n UrlSegment,\n type CanMatchFn,\n type GuardResult,\n type Route,\n type Routes,\n type UrlMatcher,\n} from '@angular/router';\nimport { MARKDOWN_PAGE, markdownPageFile } from './markdown-page.ts';\n\n/**\n * What Metro's `require.context(directory, true, pattern, mode)` returns: the files it found, each\n * keyed by its path from that directory (`./users/[id].page.ts`), and a call that loads one, which\n * returns a promise in `'lazy'` mode and the module itself in `'sync'` mode.\n */\nexport interface PageContext {\n keys(): string[];\n (key: string): unknown;\n}\n\n/**\n * The files of an `import.meta.glob`, as Vite and Vitest make it: each file's path, with a call\n * that loads it (`import.meta.glob('./pages/**\\/*.page.ts')`) or the module itself\n * (`{ eager: true }`).\n */\nexport type PageFiles = Readonly<Record<string, unknown>>;\n\nexport interface FileRoutesOptions {\n /**\n * The component a `.md` page is drawn with. It reads its file with `injectMarkdownPage()` and\n * draws `tokens` with `<markdown>` from `@ng-native/components/markdown`. Required when the\n * pages include a `.md` file.\n */\n markdownPage?: Type<unknown>;\n}\n\n/** What a page's file sets about its route by being where it is, so `routeMeta` does not. */\nconst SET_BY_THE_FILE = [\n 'path',\n 'matcher',\n 'component',\n 'loadComponent',\n 'children',\n 'loadChildren',\n 'canLoad',\n 'outlet',\n] as const;\n\n/**\n * A page's `export const routeMeta`, as Analog has it: the page's route config, less what the\n * file's place sets. `meta`, the page's HTML meta tags in Analog, is accepted and left out, since\n * there is no document head on a device. A page that only redirects has `redirectTo` and no\n * component; its `pathMatch` is `'full'` unless it says otherwise.\n */\nexport type RouteMeta =\n | (Omit<Route, (typeof SET_BY_THE_FILE)[number] | 'redirectTo' | 'pathMatch' | 'canMatch'> & {\n canMatch?: CanMatchFn[];\n meta?: unknown;\n redirectTo?: never;\n })\n | { redirectTo: NonNullable<Route['redirectTo']>; pathMatch?: Route['pathMatch'] };\n\n/** A page module: its component as the default export, and its `routeMeta`. */\ninterface PageModule {\n default?: unknown;\n routeMeta?: RouteMeta;\n}\n\n/** One part of a URL a file or folder name makes. */\ntype Part =\n | { kind: 'static'; path: string }\n | { kind: 'group' }\n | { kind: 'param'; name: string }\n | { kind: 'rest'; name: string };\n\n/** A file found, by the key it was listed under and its path from the pages folder. */\ninterface Page {\n readonly key: string;\n /** Its path from the pages folder: `users/[id].page.ts`. */\n readonly file: string;\n readonly markdown: boolean;\n /** The module, or a promise of it. Called on navigation for a lazy page, at once otherwise. */\n readonly load: () => unknown;\n readonly lazy: boolean;\n}\n\n/** A file or folder name in the pages folder, with the file it names and what is under it. */\ninterface Entry {\n readonly parts: Part[];\n readonly pages: Page[];\n readonly children: Map<string, Entry>;\n}\n\n/** A route at one level, with what it sorts by. */\ninterface Ranked {\n readonly rank: number;\n readonly path: string;\n readonly key: string;\n readonly route: Route;\n}\n\n/**\n * The routes for an app's pages, for `provideNativeRouter`.\n *\n * `pages` is the `require.context` of the pages folder, or, in a Vitest test, which has no\n * `require.context`, the files of an `import.meta.glob` such as\n * `import.meta.glob('./pages/**\\/*.page.ts')`. A page from a `require.context`, or from a glob\n * that loads each file, is loaded the first time it is navigated to; a page from an eager glob is\n * routed as it is.\n *\n * | File | Route |\n * | --------------------- | ---------------------------------------------------------- |\n * | `index.page.ts` | `''`, the folder's own page |\n * | `about.page.ts` | `about` |\n * | `[id].page.ts` | `:id` |\n * | `[...slug].page.ts` | all under its folder, the rest of the URL as `slug` |\n * | `(auth)/` | no segment of its own |\n * | `blog.[slug].page.ts` | `blog/:slug`, without a layout |\n * | `products.page.ts` | a layout around `products/`, with an outlet for its pages |\n *\n * At each level a static path comes first, then a group's layout, then a parameter, then a\n * catch-all. Two files at the same URL, a name with brackets that are not one of the above, and a\n * `.md` page with no `options.markdownPage` each throw here, naming the file. A page with no\n * default export throws here when it is eager, and on the navigation that loads it when it is\n * lazy.\n */\nexport function fileRoutes(\n pages: PageContext | PageFiles,\n options: FileRoutesOptions = {},\n): Routes {\n const found = typeof pages === 'function' ? fromContext(pages) : fromGlob(pages);\n const root: Entry = { parts: [], pages: [], children: new Map() };\n for (const [path, page] of found) {\n if (page.markdown && !options.markdownPage) {\n throw new Error(\n `${page.key} is a Markdown page, and fileRoutes has no component to draw it with. Pass ` +\n 'one: fileRoutes(pages, { markdownPage }). See injectMarkdownPage.',\n );\n }\n const names = path.split('/');\n let entry = root;\n for (const name of names) {\n let next = entry.children.get(name);\n if (!next) {\n next = { parts: partsOf(name, page.key), pages: [], children: new Map() };\n entry.children.set(name, next);\n }\n entry = next;\n }\n entry.pages.push(page);\n }\n checkPaths(root, [], new Map());\n return routesOf(root, options).map((ranked) => ranked.route);\n}\n\n/** The context's files, each by its path from the pages folder without its extension. */\nfunction fromContext(pages: PageContext): [string, Page][] {\n return pages.keys().map((key) => {\n const path = key.replace(/\\\\/g, '/').replace(/^\\.?\\//, '');\n return named(key, path, () => pages(key), true);\n });\n}\n\n/**\n * The glob's files, each by its path from the pages folder: what follows the first `pages/` in\n * its key (`/src/app/pages/about.page.ts`, `./pages/about.page.ts`), or the key itself when it\n * has none, for a glob made inside the pages folder.\n */\nfunction fromGlob(pages: PageFiles): [string, Page][] {\n return Object.keys(pages).map((key) => {\n const slashed = key.replace(/\\\\/g, '/');\n const path = /^(?:.*?\\/)?pages\\/(.*)$/.exec(slashed)?.[1] ?? slashed.replace(/^\\.?\\//, '');\n const value = pages[key];\n return typeof value === 'function'\n ? named(key, path, value as () => unknown, true)\n : named(key, path, () => value, false);\n });\n}\n\nfunction named(key: string, path: string, load: () => unknown, lazy: boolean): [string, Page] {\n const markdown = path.endsWith('.md');\n const stem = path.replace(/\\.page\\.[cm]?[jt]s$/, '').replace(/\\.md$/, '');\n if (stem === path) {\n throw new Error(\n `${key} is not a page. A page's file name ends in .page.ts, or .md for a Markdown page: ` +\n 'narrow the require.context or import.meta.glob to those.',\n );\n }\n return [stem, { key, file: path, markdown, load, lazy }];\n}\n\n/**\n * The URL parts a file or folder name makes. A dot splits a name into segments, as in Analog\n * (`blog.[slug]` is `blog/:slug`), except inside brackets (`[...slug]`).\n */\nfunction partsOf(name: string, key: string): Part[] {\n return name.split(/\\.(?![^[]*\\])/).map((piece): Part => {\n if (piece === 'index') return { kind: 'static', path: '' };\n if (/^\\([^()[\\]./]+\\)$/.test(piece)) return { kind: 'group' };\n const rest = /^\\[\\[\\.\\.\\.([\\w$-]+)\\]\\]$/.exec(piece) ?? /^\\[\\.\\.\\.([\\w$-]+)\\]$/.exec(piece);\n if (rest) return { kind: 'rest', name: rest[1]! };\n const param = /^\\[([\\w$-]+)\\]$/.exec(piece);\n if (param) return { kind: 'param', name: param[1]! };\n if (piece && !/[[\\]():]/.test(piece)) return { kind: 'static', path: piece };\n throw new Error(\n `${key}: \"${piece}\" is not a page name fileRoutes reads. A name is a segment (about), a ` +\n 'parameter ([id]), a catch-all ([...slug]) or a group ((auth)), and index is the ' +\n \"folder's own page.\",\n );\n });\n}\n\n/**\n * Throws for a catch-all with anything after it, and for two pages at one URL: two files for one\n * name (`about.page.ts` and `about.md`), or two pages whose paths differ only in groups or in\n * parameter names. A layout is not a page at its URL; its folder's `index.page.ts` is.\n */\nfunction checkPaths(entry: Entry, above: Part[], seen: Map<string, string>): void {\n for (const child of entry.children.values()) {\n const parts = [...above, ...child.parts];\n const rest = parts.findIndex((part) => part.kind === 'rest');\n const key = child.pages[0]?.key ?? firstKey(child);\n if (rest !== -1 && (rest < parts.length - 1 || child.children.size > 0)) {\n throw new Error(\n `${key}: a catch-all ([...slug]) takes the rest of the URL, so it is the last part of a ` +\n 'path, with no folder of pages under it.',\n );\n }\n if (child.pages.length > 1) {\n throw new Error(\n `${child.pages[0]!.key} and ${child.pages[1]!.key} are the same page. Keep one of them.`,\n );\n }\n if (child.pages.length === 1 && child.children.size === 0) {\n const url = urlOf(parts);\n const other = seen.get(url);\n if (other) {\n throw new Error(\n `${other} and ${key} are both the page at /${url}. Keep one of them, or move one.`,\n );\n }\n seen.set(url, key);\n }\n checkPaths(child, parts, seen);\n }\n}\n\nfunction firstKey(entry: Entry): string {\n for (const child of entry.children.values()) return child.pages[0]?.key ?? firstKey(child);\n return '';\n}\n\n/** A page's URL as a pattern, with every parameter alike, for telling two pages at one URL. */\nfunction urlOf(parts: Part[]): string {\n return parts\n .flatMap((part) => {\n if (part.kind === 'param') return [':'];\n if (part.kind === 'rest') return ['**'];\n return part.kind === 'static' && part.path ? [part.path] : [];\n })\n .join('/');\n}\n\n/**\n * The routes under `entry`, in the order Angular should try them: a static path first, a longer\n * one before a shorter it starts with, then a group's layout, then a path with a parameter, then a\n * catch-all, each by its path. A group with no layout of its own adds nothing to the URL or the\n * tree, so its routes join its siblings'.\n */\nfunction routesOf(entry: Entry, options: FileRoutesOptions): Ranked[] {\n const ranked: Ranked[] = [];\n for (const child of entry.children.values()) {\n const children = routesOf(child, options);\n const path = pathOf(child.parts);\n if (!child.pages[0] && !path) ranked.push(...children);\n else ranked.push(rankedRoute(child, path, children, options));\n }\n return ranked.sort(\n (a, b) => a.rank - b.rank || byPath(a.path, b.path) || (a.key < b.key ? -1 : 1),\n );\n}\n\n/**\n * Paths by their segments, a longer one before the path it starts with: `users/new` is tried\n * before the `users` folder, whose `:id` would take `new`, as Analog orders them.\n */\nfunction byPath(a: string, b: string): number {\n const left = a.split('/');\n const right = b.split('/');\n for (let i = 0; i < Math.min(left.length, right.length); i++) {\n if (left[i] !== right[i]) return left[i]! < right[i]! ? -1 : 1;\n }\n return right.length - left.length;\n}\n\n/** The route for a file or folder name at `path`, with its routes under it. */\nfunction rankedRoute(\n entry: Entry,\n path: string,\n children: Ranked[],\n options: FileRoutesOptions,\n): Ranked {\n const page = entry.pages[0];\n const rest = entry.parts.find((part) => part.kind === 'rest');\n const nested = children.length ? children.map((child) => child.route) : undefined;\n let route: Route = page\n ? pageRoute(page, rest ? { matcher: restOf(rest.name) } : { path }, nested, options)\n : { path, children: nested };\n if (rest && path) route = { path, children: [route] };\n const rank = rest ? 3 : path.includes(':') ? 2 : !path && nested ? 1 : 0;\n return { rank, path, key: page?.key ?? firstKey(entry), route };\n}\n\n/** The path a name's parts make, without its groups, its index or its catch-all. */\nfunction pathOf(parts: Part[]): string {\n return parts\n .flatMap((part) => (part.kind === 'static' || part.kind === 'param' ? [segmentOf(part)] : []))\n .filter(Boolean)\n .join('/');\n}\n\nfunction segmentOf(part: Part): string {\n if (part.kind === 'param') return `:${part.name}`;\n return part.kind === 'static' ? part.path : '';\n}\n\n/**\n * A catch-all's matcher: it takes every segment left, none included, as Analog's `**` does, and\n * hands them over as one parameter, `docs/intro` for `/docs/intro` under the catch-all's folder.\n */\nfunction restOf(name: string): UrlMatcher {\n return (segments) => ({\n consumed: segments,\n posParams: { [name]: new UrlSegment(segments.map((segment) => segment.path).join('/'), {}) },\n });\n}\n\n/**\n * A page's route, at `at`: one with no component that takes the page's place in the URL, and the\n * page's own route under it at `''`, as Analog makes it, so the page's `routeMeta` can wait for a\n * lazy page to load. The page's `children`, when it is a layout, go under its own route.\n *\n * `canMatch` is on the outer route, not the page's own: Angular takes a route whose children match\n * none of an empty rest of the URL as matched, so a page refused there would leave an empty screen\n * where the next route that matches should be. An index page's outer route matches only the whole\n * URL, so its guards, and its load when it is lazy, run for no other page's URL: a guard that\n * redirects would otherwise run again for the URL it sent the router to.\n */\nfunction pageRoute(\n page: Page,\n at: Pick<Route, 'path' | 'matcher' | 'pathMatch'>,\n children: Routes | undefined,\n options: FileRoutesOptions,\n): Route {\n if (at.path === '' && !children) at = { ...at, pathMatch: 'full' };\n if (!page.lazy) {\n const { canMatch, ...own } = ownRoute(page, page.load(), children, options);\n return { ...at, ...(canMatch ? { canMatch } : {}), children: [own] };\n }\n const load = loaderOf(page);\n const loadChildren = async () => {\n const { canMatch: _matchedAbove, ...own } = ownRoute(page, await load(), children, options);\n return [own];\n };\n if (page.markdown) return { ...at, loadChildren };\n const canMatch: CanMatchFn = (...args) => {\n const injector = inject(EnvironmentInjector);\n return load().then((module) => passes((module as PageModule)?.routeMeta, injector, args));\n };\n return { ...at, canMatch: [canMatch], loadChildren };\n}\n\n/**\n * Loads a lazy page once for its `canMatch` and its routes both. A load that fails is forgotten,\n * so the next navigation tries again, as Angular does with a `loadChildren` that fails.\n */\nfunction loaderOf(page: Page): () => Promise<unknown> {\n let loading: Promise<unknown> | undefined;\n return () =>\n (loading ??= Promise.resolve()\n .then(page.load)\n .catch((error: unknown) => {\n loading = undefined;\n throw error;\n }));\n}\n\n/** Runs a loaded page's `canMatch` guards in turn, until one says other than `true`. */\nasync function passes(\n meta: RouteMeta | undefined,\n injector: EnvironmentInjector,\n args: Parameters<CanMatchFn>,\n): Promise<GuardResult> {\n const guards = (meta as { canMatch?: CanMatchFn[] } | undefined)?.canMatch ?? [];\n for (const guard of guards) {\n const result = await firstOf(runInInjectionContext(injector, () => guard(...args)));\n if (result !== true) return result as GuardResult;\n }\n return true;\n}\n\n/** A guard's result, from an Observable's first value or a promise as from a value. */\nfunction firstOf(result: unknown): Promise<unknown> {\n const observable = result as { subscribe?: unknown } | null;\n if (typeof observable?.subscribe !== 'function') return Promise.resolve(result);\n const source = observable as { subscribe(observer: object): { unsubscribe(): void } };\n return new Promise((resolve, reject) => {\n let done = false;\n let subscription: { unsubscribe(): void } | undefined;\n subscription = source.subscribe({\n next: (value: unknown) => {\n if (done) return;\n done = true;\n resolve(value);\n subscription?.unsubscribe();\n },\n error: (error: unknown) => {\n if (done) return;\n done = true;\n reject(error);\n },\n complete: () => {\n if (done) return;\n done = true;\n resolve(false);\n },\n });\n if (done) subscription.unsubscribe();\n });\n}\n\nfunction ownRoute(\n page: Page,\n loaded: unknown,\n children: Routes | undefined,\n options: FileRoutesOptions,\n): Route {\n const module = page.markdown ? markdownModule(page, loaded, options) : (loaded as PageModule);\n const meta = (module?.routeMeta ?? {}) as Record<string, unknown>;\n const set = SET_BY_THE_FILE.find((name) => name in meta);\n if (set) {\n throw new Error(\n `${page.key} sets routeMeta.${set}, which a page's file sets by where it is. Leave it out.`,\n );\n }\n const config = { ...meta } as Route;\n delete (config as { meta?: unknown }).meta;\n if (config.redirectTo !== undefined) {\n if (children) {\n throw new Error(\n `${page.key} redirects, and a page that redirects is not a layout: its folder's pages ` +\n 'would never show. Redirect from a page with no folder beside it.',\n );\n }\n return { path: '', pathMatch: 'full', ...config };\n }\n if (typeof module?.default !== 'function') {\n throw new Error(\n `${page.key} has no default export. A page exports its component as the default: ` +\n '`export default class AboutPage {}`.',\n );\n }\n return {\n path: '',\n component: module.default as Type<unknown>,\n runGuardsAndResolvers: 'paramsOrQueryParamsChange',\n ...config,\n ...(children ? { children } : {}),\n };\n}\n\n/** A `.md` page as a page module: `markdownPage`, titled by the file's front matter. */\nfunction markdownModule(page: Page, loaded: unknown, options: FileRoutesOptions): PageModule {\n const file = markdownPageFile(page.file, loaded);\n const title = file.attributes['title'];\n return {\n default: options.markdownPage,\n routeMeta: {\n ...(typeof title === 'string' ? { title } : {}),\n data: { [MARKDOWN_PAGE]: file },\n },\n };\n}\n"]}
|
package/dist/in-tab.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tab a page is in, for a stack inside it to hear when another tab comes in front.
|
|
3
|
+
*
|
|
4
|
+
* On iOS a sheet or a modal presented in a tab's own stack is shown over the whole window, the tab
|
|
5
|
+
* bar included, so the tab cannot keep it up while it is behind: it would cover the tab in front.
|
|
6
|
+
* The tab's stacks dismiss what they presented instead, and the tab is remembered at the page
|
|
7
|
+
* beneath. On Android react-native-screens draws it inside the tab's own stack, a modal as a push
|
|
8
|
+
* and a form sheet as a bottom sheet, so the bar stays in reach; it is dismissed there too, so a
|
|
9
|
+
* tab comes back on the same page on both platforms.
|
|
10
|
+
*/
|
|
11
|
+
import { InjectionToken } from '@angular/core';
|
|
12
|
+
export interface InTab {
|
|
13
|
+
/**
|
|
14
|
+
* Runs `leave` each time another tab of the bar comes in front of this one. `leave` answers the
|
|
15
|
+
* url the tab is left on once it has dismissed what it presented, or null when it dismissed
|
|
16
|
+
* nothing. Answers the unsubscribe.
|
|
17
|
+
*/
|
|
18
|
+
onLeave(leave: () => string | null): () => void;
|
|
19
|
+
}
|
|
20
|
+
export declare const IN_TAB: InjectionToken<InTab>;
|
package/dist/in-tab.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tab a page is in, for a stack inside it to hear when another tab comes in front.
|
|
3
|
+
*
|
|
4
|
+
* On iOS a sheet or a modal presented in a tab's own stack is shown over the whole window, the tab
|
|
5
|
+
* bar included, so the tab cannot keep it up while it is behind: it would cover the tab in front.
|
|
6
|
+
* The tab's stacks dismiss what they presented instead, and the tab is remembered at the page
|
|
7
|
+
* beneath. On Android react-native-screens draws it inside the tab's own stack, a modal as a push
|
|
8
|
+
* and a form sheet as a bottom sheet, so the bar stays in reach; it is dismissed there too, so a
|
|
9
|
+
* tab comes back on the same page on both platforms.
|
|
10
|
+
*/
|
|
11
|
+
import { InjectionToken } from '@angular/core';
|
|
12
|
+
export const IN_TAB = new InjectionToken('ng-native.router.in-tab');
|
|
13
|
+
//# sourceMappingURL=in-tab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"in-tab.js","sourceRoot":"","sources":["../src/in-tab.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAW/C,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,cAAc,CAAQ,yBAAyB,CAAC,CAAC","sourcesContent":["/**\n * The tab a page is in, for a stack inside it to hear when another tab comes in front.\n *\n * On iOS a sheet or a modal presented in a tab's own stack is shown over the whole window, the tab\n * bar included, so the tab cannot keep it up while it is behind: it would cover the tab in front.\n * The tab's stacks dismiss what they presented instead, and the tab is remembered at the page\n * beneath. On Android react-native-screens draws it inside the tab's own stack, a modal as a push\n * and a form sheet as a bottom sheet, so the bar stays in reach; it is dismissed there too, so a\n * tab comes back on the same page on both platforms.\n */\nimport { InjectionToken } from '@angular/core';\n\nexport interface InTab {\n /**\n * Runs `leave` each time another tab of the bar comes in front of this one. `leave` answers the\n * url the tab is left on once it has dismissed what it presented, or null when it dismissed\n * nothing. Answers the unsubscribe.\n */\n onLeave(leave: () => string | null): () => void;\n}\n\nexport const IN_TAB = new InjectionToken<InTab>('ng-native.router.in-tab');\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export { NATIVE_HEADER_DEFAULTS, NATIVE_TAB_DEFAULTS, type HeaderDefaults, type
|
|
|
14
14
|
export { NativeHeader, type BackButtonDisplayMode, type BlurEffect, type HeaderDirection, type HeaderInterfaceStyle, } from './native-header.ts';
|
|
15
15
|
export { DEFAULT_HEADER_PALETTE, NATIVE_HEADER_PALETTE, type HeaderColors, } from './native-header-palette.ts';
|
|
16
16
|
export { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';
|
|
17
|
+
export { NativeToolbar, NativeToolbarItem, type ToolbarItemType } from './native-toolbar.ts';
|
|
17
18
|
export { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';
|
|
18
19
|
export { NativeStackOutlet } from './native-stack-outlet.ts';
|
|
19
20
|
export { NativeTab, type TabAppearance, type TabBarBlurEffect, type TabIcon, type TabIconProps, type TabItemAppearance, type TabStateAppearance, type TabSystemItem, } from './native-tab.ts';
|
|
@@ -24,4 +25,6 @@ export { NativeStackReuseStrategy, reuseScreen } from './native-stack-reuse-stra
|
|
|
24
25
|
export { provideNativeRouter, withHeaderDefaults, withLinkParent, withTabDefaults, type NativeRouterFeature, } from './provide-native-router.ts';
|
|
25
26
|
export { type GestureResponseDistance, type ReplaceAnimation, type ScreenPresentation, type StackAnimation, type StackPresentation, type SwipeDirection, } from './screen-presentation.ts';
|
|
26
27
|
export { ActivityState, type ActivityStateValue } from './screens.ts';
|
|
28
|
+
export { fileRoutes, type FileRoutesOptions, type PageContext, type PageFiles, type RouteMeta, } from './file-routes.ts';
|
|
29
|
+
export { MARKDOWN_PAGE, injectMarkdownPage, markdownPageFile, type MarkdownPageFile, } from './markdown-page.ts';
|
|
27
30
|
export { FullWindowOverlay } from './full-window-overlay.ts';
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,7 @@ export { NATIVE_HEADER_DEFAULTS, NATIVE_TAB_DEFAULTS, } from "./native-bar-defau
|
|
|
14
14
|
export { NativeHeader, } from "./native-header.js";
|
|
15
15
|
export { DEFAULT_HEADER_PALETTE, NATIVE_HEADER_PALETTE, } from "./native-header-palette.js";
|
|
16
16
|
export { NativeHeaderItem } from "./native-header-item.js";
|
|
17
|
+
export { NativeToolbar, NativeToolbarItem } from "./native-toolbar.js";
|
|
17
18
|
export { NativeSearchBar } from "./native-search-bar.js";
|
|
18
19
|
export { NativeStackOutlet } from "./native-stack-outlet.js";
|
|
19
20
|
export { NativeTab, } from "./native-tab.js";
|
|
@@ -24,5 +25,7 @@ export { NativeStackReuseStrategy, reuseScreen } from "./native-stack-reuse-stra
|
|
|
24
25
|
export { provideNativeRouter, withHeaderDefaults, withLinkParent, withTabDefaults, } from "./provide-native-router.js";
|
|
25
26
|
export {} from "./screen-presentation.js";
|
|
26
27
|
export { ActivityState } from "./screens.js";
|
|
28
|
+
export { fileRoutes, } from "./file-routes.js";
|
|
29
|
+
export { MARKDOWN_PAGE, injectMarkdownPage, markdownPageFile, } from "./markdown-page.js";
|
|
27
30
|
export { FullWindowOverlay } from "./full-window-overlay.js";
|
|
28
31
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,gBAAgB,GAIjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAmB,MAAM,+BAA+B,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,mBAAmB,GAIpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,YAAY,GAKb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,eAAe,EAA2B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EACL,SAAS,GAQV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAA2B,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAE,eAAe,EAAwB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACzF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,eAAe,GAEhB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAON,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * `@angular/router` on a native stack: the outlet that maps activated routes onto `RNSScreen`,\n * the `PlatformLocation` that stands in for the browser's history, and the reuse strategy that\n * keeps a popped screen's component alive while it animates out.\n *\n * Two of these are decorated, so the same caveat as the components barrel applies: a Node test\n * reaches the source file, not this.\n */\nexport { followLink, type LinkParent } from './native-links.ts';\nexport {\n NativeNavigation,\n type NativeNavigationOptions,\n type NavigationCommands,\n type PresentOptions,\n} from './native-navigation.ts';\nexport { NativePlatformLocation, type LinkSource } from './native-platform-location.ts';\nexport { NativeRouterLink } from './native-router-link.ts';\nexport {\n NATIVE_HEADER_DEFAULTS,\n NATIVE_TAB_DEFAULTS,\n type HeaderDefaults,\n type SchemeDefaults,\n type TabDefaults,\n} from './native-bar-defaults.ts';\nexport {\n NativeHeader,\n type BackButtonDisplayMode,\n type BlurEffect,\n type HeaderDirection,\n type HeaderInterfaceStyle,\n} from './native-header.ts';\nexport {\n DEFAULT_HEADER_PALETTE,\n NATIVE_HEADER_PALETTE,\n type HeaderColors,\n} from './native-header-palette.ts';\nexport { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';\nexport { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';\nexport { NativeStackOutlet } from './native-stack-outlet.ts';\nexport {\n NativeTab,\n type TabAppearance,\n type TabBarBlurEffect,\n type TabIcon,\n type TabIconProps,\n type TabItemAppearance,\n type TabStateAppearance,\n type TabSystemItem,\n} from './native-tab.ts';\nexport { NativeTabsOutlet } from './native-tabs-outlet.ts';\nexport { ScreenSafeAreaView, type ScreenSafeAreaEdge } from './screen-safe-area-view.ts';\nexport { TabSafeAreaView, type TabSafeAreaEdge } from './tab-safe-area-view.ts';\nexport { NativeStackReuseStrategy, reuseScreen } from './native-stack-reuse-strategy.ts';\nexport {\n provideNativeRouter,\n withHeaderDefaults,\n withLinkParent,\n withTabDefaults,\n type NativeRouterFeature,\n} from './provide-native-router.ts';\nexport {\n type GestureResponseDistance,\n type ReplaceAnimation,\n type ScreenPresentation,\n type StackAnimation,\n type StackPresentation,\n type SwipeDirection,\n} from './screen-presentation.ts';\nexport { ActivityState, type ActivityStateValue } from './screens.ts';\nexport { FullWindowOverlay } from './full-window-overlay.ts';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,UAAU,EAAmB,MAAM,mBAAmB,CAAC;AAChE,OAAO,EACL,gBAAgB,GAIjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAmB,MAAM,+BAA+B,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EACL,sBAAsB,EACtB,mBAAmB,GAIpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,YAAY,GAKb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,sBAAsB,EACtB,qBAAqB,GAEtB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,gBAAgB,EAAuB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAwB,MAAM,qBAAqB,CAAC;AAC7F,OAAO,EAAE,eAAe,EAA2B,MAAM,wBAAwB,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EACL,SAAS,GAQV,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAA2B,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAE,eAAe,EAAwB,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AACzF,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,eAAe,GAEhB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAON,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EACL,UAAU,GAKX,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC","sourcesContent":["/**\n * `@angular/router` on a native stack: the outlet that maps activated routes onto `RNSScreen`,\n * the `PlatformLocation` that stands in for the browser's history, and the reuse strategy that\n * keeps a popped screen's component alive while it animates out.\n *\n * Two of these are decorated, so the same caveat as the components barrel applies: a Node test\n * reaches the source file, not this.\n */\nexport { followLink, type LinkParent } from './native-links.ts';\nexport {\n NativeNavigation,\n type NativeNavigationOptions,\n type NavigationCommands,\n type PresentOptions,\n} from './native-navigation.ts';\nexport { NativePlatformLocation, type LinkSource } from './native-platform-location.ts';\nexport { NativeRouterLink } from './native-router-link.ts';\nexport {\n NATIVE_HEADER_DEFAULTS,\n NATIVE_TAB_DEFAULTS,\n type HeaderDefaults,\n type SchemeDefaults,\n type TabDefaults,\n} from './native-bar-defaults.ts';\nexport {\n NativeHeader,\n type BackButtonDisplayMode,\n type BlurEffect,\n type HeaderDirection,\n type HeaderInterfaceStyle,\n} from './native-header.ts';\nexport {\n DEFAULT_HEADER_PALETTE,\n NATIVE_HEADER_PALETTE,\n type HeaderColors,\n} from './native-header-palette.ts';\nexport { NativeHeaderItem, type HeaderItemType } from './native-header-item.ts';\nexport { NativeToolbar, NativeToolbarItem, type ToolbarItemType } from './native-toolbar.ts';\nexport { NativeSearchBar, type SearchBarPlacement } from './native-search-bar.ts';\nexport { NativeStackOutlet } from './native-stack-outlet.ts';\nexport {\n NativeTab,\n type TabAppearance,\n type TabBarBlurEffect,\n type TabIcon,\n type TabIconProps,\n type TabItemAppearance,\n type TabStateAppearance,\n type TabSystemItem,\n} from './native-tab.ts';\nexport { NativeTabsOutlet } from './native-tabs-outlet.ts';\nexport { ScreenSafeAreaView, type ScreenSafeAreaEdge } from './screen-safe-area-view.ts';\nexport { TabSafeAreaView, type TabSafeAreaEdge } from './tab-safe-area-view.ts';\nexport { NativeStackReuseStrategy, reuseScreen } from './native-stack-reuse-strategy.ts';\nexport {\n provideNativeRouter,\n withHeaderDefaults,\n withLinkParent,\n withTabDefaults,\n type NativeRouterFeature,\n} from './provide-native-router.ts';\nexport {\n type GestureResponseDistance,\n type ReplaceAnimation,\n type ScreenPresentation,\n type StackAnimation,\n type StackPresentation,\n type SwipeDirection,\n} from './screen-presentation.ts';\nexport { ActivityState, type ActivityStateValue } from './screens.ts';\nexport {\n fileRoutes,\n type FileRoutesOptions,\n type PageContext,\n type PageFiles,\n type RouteMeta,\n} from './file-routes.ts';\nexport {\n MARKDOWN_PAGE,\n injectMarkdownPage,\n markdownPageFile,\n type MarkdownPageFile,\n} from './markdown-page.ts';\nexport { FullWindowOverlay } from './full-window-overlay.ts';\n"]}
|