@buoy-gg/route-events 1.7.2
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 +654 -0
- package/lib/commonjs/RouteObserver.js +54 -0
- package/lib/commonjs/RouteParser.js +310 -0
- package/lib/commonjs/RouteTracker.js +39 -0
- package/lib/commonjs/components/NavigationStack.js +584 -0
- package/lib/commonjs/components/RouteEventDetailContent.js +492 -0
- package/lib/commonjs/components/RouteEventExpandedContent.js +187 -0
- package/lib/commonjs/components/RouteEventItemCompact.js +175 -0
- package/lib/commonjs/components/RouteEventsModalWithTabs.js +560 -0
- package/lib/commonjs/components/RouteEventsTimeline.js +82 -0
- package/lib/commonjs/components/RouteFilterViewV2.js +42 -0
- package/lib/commonjs/components/RoutesSitemap.js +948 -0
- package/lib/commonjs/expoRouterStore.js +104 -0
- package/lib/commonjs/index.js +99 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/preset.js +83 -0
- package/lib/commonjs/useNavigationStack.js +241 -0
- package/lib/commonjs/useRouteObserver.js +73 -0
- package/lib/commonjs/useRouteSitemap.js +234 -0
- package/lib/commonjs/utils/safeExpoRouter.js +129 -0
- package/lib/commonjs/utils/safeReactNavigation.js +104 -0
- package/lib/module/RouteObserver.js +49 -0
- package/lib/module/RouteParser.js +305 -0
- package/lib/module/RouteTracker.js +35 -0
- package/lib/module/components/NavigationStack.js +580 -0
- package/lib/module/components/RouteEventDetailContent.js +487 -0
- package/lib/module/components/RouteEventExpandedContent.js +183 -0
- package/lib/module/components/RouteEventItemCompact.js +171 -0
- package/lib/module/components/RouteEventsModalWithTabs.js +557 -0
- package/lib/module/components/RouteEventsTimeline.js +78 -0
- package/lib/module/components/RouteFilterViewV2.js +38 -0
- package/lib/module/components/RoutesSitemap.js +944 -0
- package/lib/module/expoRouterStore.js +98 -0
- package/lib/module/index.js +23 -0
- package/lib/module/preset.js +79 -0
- package/lib/module/useNavigationStack.js +238 -0
- package/lib/module/useRouteObserver.js +70 -0
- package/lib/module/useRouteSitemap.js +229 -0
- package/lib/module/utils/safeExpoRouter.js +120 -0
- package/lib/module/utils/safeReactNavigation.js +98 -0
- package/lib/typescript/RouteObserver.d.ts +37 -0
- package/lib/typescript/RouteObserver.d.ts.map +1 -0
- package/lib/typescript/RouteParser.d.ts +129 -0
- package/lib/typescript/RouteParser.d.ts.map +1 -0
- package/lib/typescript/RouteTracker.d.ts +29 -0
- package/lib/typescript/RouteTracker.d.ts.map +1 -0
- package/lib/typescript/components/NavigationStack.d.ts +11 -0
- package/lib/typescript/components/NavigationStack.d.ts.map +1 -0
- package/lib/typescript/components/RouteEventDetailContent.d.ts +21 -0
- package/lib/typescript/components/RouteEventDetailContent.d.ts.map +1 -0
- package/lib/typescript/components/RouteEventExpandedContent.d.ts +16 -0
- package/lib/typescript/components/RouteEventExpandedContent.d.ts.map +1 -0
- package/lib/typescript/components/RouteEventItemCompact.d.ts +15 -0
- package/lib/typescript/components/RouteEventItemCompact.d.ts.map +1 -0
- package/lib/typescript/components/RouteEventsModalWithTabs.d.ts +15 -0
- package/lib/typescript/components/RouteEventsModalWithTabs.d.ts.map +1 -0
- package/lib/typescript/components/RouteEventsTimeline.d.ts +17 -0
- package/lib/typescript/components/RouteEventsTimeline.d.ts.map +1 -0
- package/lib/typescript/components/RouteFilterViewV2.d.ts +9 -0
- package/lib/typescript/components/RouteFilterViewV2.d.ts.map +1 -0
- package/lib/typescript/components/RoutesSitemap.d.ts +15 -0
- package/lib/typescript/components/RoutesSitemap.d.ts.map +1 -0
- package/lib/typescript/expoRouterStore.d.ts +28 -0
- package/lib/typescript/expoRouterStore.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +18 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/preset.d.ts +76 -0
- package/lib/typescript/preset.d.ts.map +1 -0
- package/lib/typescript/useNavigationStack.d.ts +48 -0
- package/lib/typescript/useNavigationStack.d.ts.map +1 -0
- package/lib/typescript/useRouteObserver.d.ts +27 -0
- package/lib/typescript/useRouteObserver.d.ts.map +1 -0
- package/lib/typescript/useRouteSitemap.d.ts +102 -0
- package/lib/typescript/useRouteSitemap.d.ts.map +1 -0
- package/lib/typescript/utils/safeExpoRouter.d.ts +13 -0
- package/lib/typescript/utils/safeExpoRouter.d.ts.map +1 -0
- package/lib/typescript/utils/safeReactNavigation.d.ts +10 -0
- package/lib/typescript/utils/safeReactNavigation.d.ts.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.RouteParser = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* RouteParser - Extract and organize routes from Expo Router's RouteNode tree
|
|
9
|
+
*
|
|
10
|
+
* Based on research findings in:
|
|
11
|
+
* - docs/routing/expo/ROUTENODE_TYPE_DEFINITION.md
|
|
12
|
+
* - docs/routing/expo/ROUTES_SITEMAP_RESEARCH.md
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// Type-only definition to avoid Metro resolution issues
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Types & Interfaces
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Route type classification
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
// 404 fallback
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Parsed route information
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Grouped routes for display
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Route statistics
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Route Parser Class
|
|
41
|
+
// ============================================================================
|
|
42
|
+
|
|
43
|
+
class RouteParser {
|
|
44
|
+
/**
|
|
45
|
+
* Parse RouteNode tree and extract all routes
|
|
46
|
+
*/
|
|
47
|
+
static parseRouteTree(rootNode) {
|
|
48
|
+
if (!rootNode) {
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
const routes = [];
|
|
52
|
+
this.traverseNode(rootNode, '', routes, 0);
|
|
53
|
+
return routes;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Recursively traverse RouteNode tree
|
|
58
|
+
*/
|
|
59
|
+
static traverseNode(node, parentPath, routes, depth) {
|
|
60
|
+
const currentPath = this.buildPath(node, parentPath);
|
|
61
|
+
const routeType = this.detectRouteType(node);
|
|
62
|
+
const shouldInclude = this.shouldIncludeRoute(node);
|
|
63
|
+
const routeInfo = {
|
|
64
|
+
path: currentPath,
|
|
65
|
+
name: node.route,
|
|
66
|
+
type: routeType,
|
|
67
|
+
params: this.extractParams(node),
|
|
68
|
+
nodeType: node.type,
|
|
69
|
+
contextKey: node.contextKey,
|
|
70
|
+
isInternal: node.internal ?? false,
|
|
71
|
+
children: [],
|
|
72
|
+
depth
|
|
73
|
+
};
|
|
74
|
+
if (shouldInclude) {
|
|
75
|
+
routes.push(routeInfo);
|
|
76
|
+
}
|
|
77
|
+
for (const child of node.children) {
|
|
78
|
+
this.traverseNode(child, currentPath, shouldInclude ? routeInfo.children : routes, depth + 1);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Build full path for a route node
|
|
84
|
+
*/
|
|
85
|
+
static buildPath(node, parentPath) {
|
|
86
|
+
if (node.type === 'layout' && node.route === '_layout') {
|
|
87
|
+
return parentPath;
|
|
88
|
+
}
|
|
89
|
+
if (node.route.startsWith('(') && node.route.endsWith(')')) {
|
|
90
|
+
return parentPath;
|
|
91
|
+
}
|
|
92
|
+
if (node.route === 'index') {
|
|
93
|
+
return parentPath || '/';
|
|
94
|
+
}
|
|
95
|
+
const segment = node.route;
|
|
96
|
+
if (!parentPath || parentPath === '/') {
|
|
97
|
+
return `/${segment}`;
|
|
98
|
+
}
|
|
99
|
+
return `${parentPath}/${segment}`;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Detect the route type classification
|
|
104
|
+
*/
|
|
105
|
+
static detectRouteType(node) {
|
|
106
|
+
const routeName = node.route;
|
|
107
|
+
if (node.dynamic?.some(d => d.notFound)) {
|
|
108
|
+
return 'not-found';
|
|
109
|
+
}
|
|
110
|
+
if (node.dynamic?.some(d => d.deep)) {
|
|
111
|
+
return 'catch-all';
|
|
112
|
+
}
|
|
113
|
+
if (node.dynamic && node.dynamic.length > 0) {
|
|
114
|
+
return 'dynamic';
|
|
115
|
+
}
|
|
116
|
+
if (node.type === 'layout') {
|
|
117
|
+
return 'layout';
|
|
118
|
+
}
|
|
119
|
+
if (routeName.startsWith('(') && routeName.endsWith(')')) {
|
|
120
|
+
return 'group';
|
|
121
|
+
}
|
|
122
|
+
if (routeName === 'index') {
|
|
123
|
+
return 'index';
|
|
124
|
+
}
|
|
125
|
+
return 'static';
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Extract dynamic parameter names from route
|
|
130
|
+
*/
|
|
131
|
+
static extractParams(node) {
|
|
132
|
+
if (!node.dynamic || node.dynamic.length === 0) {
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
return node.dynamic.map(d => d.name);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Determine if route should be included in results
|
|
140
|
+
*/
|
|
141
|
+
static shouldIncludeRoute(node) {
|
|
142
|
+
if (node.internal) return false;
|
|
143
|
+
if (node.generated) return false;
|
|
144
|
+
if (node.type === 'api') return false;
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Organize routes into groups for display
|
|
150
|
+
*/
|
|
151
|
+
static organizeRoutes(routes) {
|
|
152
|
+
const groups = [];
|
|
153
|
+
const rootRoutes = routes.filter(r => r.depth === 0 && r.type !== 'dynamic' && r.type !== 'layout');
|
|
154
|
+
const dynamicRoutes = this.flattenRoutes(routes).filter(r => r.type === 'dynamic' || r.type === 'catch-all');
|
|
155
|
+
const layoutRoutes = this.flattenRoutes(routes).filter(r => r.type === 'layout');
|
|
156
|
+
const groupedRoutes = this.flattenRoutes(routes).filter(r => r.type === 'group');
|
|
157
|
+
if (rootRoutes.length > 0) {
|
|
158
|
+
groups.push({
|
|
159
|
+
title: 'Root Routes',
|
|
160
|
+
icon: '',
|
|
161
|
+
routes: rootRoutes
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (dynamicRoutes.length > 0) {
|
|
165
|
+
groups.push({
|
|
166
|
+
title: 'Dynamic Routes',
|
|
167
|
+
icon: '',
|
|
168
|
+
description: 'Routes that accept parameters',
|
|
169
|
+
routes: dynamicRoutes
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
if (layoutRoutes.length > 0) {
|
|
173
|
+
groups.push({
|
|
174
|
+
title: 'Layouts',
|
|
175
|
+
icon: '',
|
|
176
|
+
description: 'Shared UI for nested screens',
|
|
177
|
+
routes: layoutRoutes
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (groupedRoutes.length > 0) {
|
|
181
|
+
groups.push({
|
|
182
|
+
title: 'Route Groups',
|
|
183
|
+
icon: '',
|
|
184
|
+
routes: groupedRoutes
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return groups;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Flatten nested routes into a single array
|
|
192
|
+
*/
|
|
193
|
+
static flattenRoutes(routes) {
|
|
194
|
+
const flattened = [];
|
|
195
|
+
function traverse(route) {
|
|
196
|
+
flattened.push(route);
|
|
197
|
+
route.children.forEach(traverse);
|
|
198
|
+
}
|
|
199
|
+
routes.forEach(traverse);
|
|
200
|
+
return flattened;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get route statistics
|
|
205
|
+
*/
|
|
206
|
+
static getRouteStats(routes) {
|
|
207
|
+
const flatRoutes = this.flattenRoutes(routes);
|
|
208
|
+
return {
|
|
209
|
+
total: flatRoutes.length,
|
|
210
|
+
static: flatRoutes.filter(r => r.type === 'static').length,
|
|
211
|
+
dynamic: flatRoutes.filter(r => r.type === 'dynamic').length,
|
|
212
|
+
catchAll: flatRoutes.filter(r => r.type === 'catch-all').length,
|
|
213
|
+
layouts: flatRoutes.filter(r => r.type === 'layout').length,
|
|
214
|
+
groups: flatRoutes.filter(r => r.type === 'group').length
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Search/filter routes by query
|
|
220
|
+
*/
|
|
221
|
+
static filterRoutes(routes, query) {
|
|
222
|
+
if (!query) {
|
|
223
|
+
return routes;
|
|
224
|
+
}
|
|
225
|
+
const lowerQuery = query.toLowerCase();
|
|
226
|
+
const flatRoutes = this.flattenRoutes(routes);
|
|
227
|
+
return flatRoutes.filter(route => {
|
|
228
|
+
return route.path.toLowerCase().includes(lowerQuery) || route.name.toLowerCase().includes(lowerQuery) || route.type.toLowerCase().includes(lowerQuery) || route.params.some(p => p.toLowerCase().includes(lowerQuery));
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Build a visual tree string representation
|
|
234
|
+
*/
|
|
235
|
+
static buildTreeString(routes, depth = 0) {
|
|
236
|
+
let output = '';
|
|
237
|
+
routes.forEach((route, index) => {
|
|
238
|
+
const isLast = index === routes.length - 1;
|
|
239
|
+
const prefix = this.buildTreePrefix(depth, isLast);
|
|
240
|
+
let routeLine = `${prefix}${route.path}`;
|
|
241
|
+
if (route.params.length > 0) {
|
|
242
|
+
routeLine += ` { ${route.params.join(', ')} }`;
|
|
243
|
+
}
|
|
244
|
+
output += routeLine + '\n';
|
|
245
|
+
if (route.children.length > 0) {
|
|
246
|
+
output += this.buildTreeString(route.children, depth + 1);
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
return output;
|
|
250
|
+
}
|
|
251
|
+
static buildTreePrefix(depth, isLast) {
|
|
252
|
+
if (depth === 0) {
|
|
253
|
+
return '';
|
|
254
|
+
}
|
|
255
|
+
const indent = ' '.repeat(depth - 1);
|
|
256
|
+
const branch = isLast ? '└─ ' : '├─ ';
|
|
257
|
+
return indent + branch;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Sort routes by various criteria
|
|
262
|
+
*/
|
|
263
|
+
static sortRoutes(routes, sortBy = 'path') {
|
|
264
|
+
const sorted = [...routes];
|
|
265
|
+
sorted.sort((a, b) => {
|
|
266
|
+
switch (sortBy) {
|
|
267
|
+
case 'path':
|
|
268
|
+
return a.path.localeCompare(b.path);
|
|
269
|
+
case 'type':
|
|
270
|
+
return a.type.localeCompare(b.type);
|
|
271
|
+
case 'name':
|
|
272
|
+
return a.name.localeCompare(b.name);
|
|
273
|
+
default:
|
|
274
|
+
return 0;
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
return sorted;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Get route by path
|
|
282
|
+
*/
|
|
283
|
+
static findRouteByPath(routes, path) {
|
|
284
|
+
const flatRoutes = this.flattenRoutes(routes);
|
|
285
|
+
return flatRoutes.find(r => r.path === path) || null;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Get all parent routes for a given route
|
|
290
|
+
*/
|
|
291
|
+
static getParentRoutes(routes, targetPath) {
|
|
292
|
+
const parents = [];
|
|
293
|
+
const flatRoutes = this.flattenRoutes(routes);
|
|
294
|
+
const target = flatRoutes.find(r => r.path === targetPath);
|
|
295
|
+
if (!target) {
|
|
296
|
+
return parents;
|
|
297
|
+
}
|
|
298
|
+
const pathSegments = targetPath.split('/').filter(Boolean);
|
|
299
|
+
let currentPath = '';
|
|
300
|
+
for (let i = 0; i < pathSegments.length - 1; i++) {
|
|
301
|
+
currentPath += '/' + pathSegments[i];
|
|
302
|
+
const parent = flatRoutes.find(r => r.path === currentPath);
|
|
303
|
+
if (parent) {
|
|
304
|
+
parents.push(parent);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return parents;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
exports.RouteParser = RouteParser;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.RouteTracker = RouteTracker;
|
|
7
|
+
var _useRouteObserver = require("./useRouteObserver");
|
|
8
|
+
/**
|
|
9
|
+
* RouteTracker - A component to place inside your navigation tree
|
|
10
|
+
*
|
|
11
|
+
* This component calls useRouteObserver() which uses expo-router hooks
|
|
12
|
+
* (usePathname, useSegments, etc.) to track navigation changes.
|
|
13
|
+
*
|
|
14
|
+
* IMPORTANT: This component MUST be placed inside your navigation tree
|
|
15
|
+
* (as a child of Stack, Tabs, or Slot) for route tracking to work.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* // In your _layout.tsx
|
|
20
|
+
* import { RouteTracker } from '@buoy-gg/route-events';
|
|
21
|
+
*
|
|
22
|
+
* export default function RootLayout() {
|
|
23
|
+
* return (
|
|
24
|
+
* <>
|
|
25
|
+
* <Stack>
|
|
26
|
+
* <Stack.Screen name="(tabs)" />
|
|
27
|
+
* </Stack>
|
|
28
|
+
* <RouteTracker />
|
|
29
|
+
* <FloatingDevTools ... />
|
|
30
|
+
* </>
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
function RouteTracker() {
|
|
37
|
+
(0, _useRouteObserver.useRouteObserver)();
|
|
38
|
+
return null;
|
|
39
|
+
}
|