@teardown/navigation-metro 2.0.80 → 2.0.82
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/dist/generator/generator.d.ts.map +1 -1
- package/dist/generator/generator.js +7 -0
- package/dist/generator/route-generator.d.ts +5 -0
- package/dist/generator/route-generator.d.ts.map +1 -1
- package/dist/generator/route-generator.js +67 -0
- package/dist/scanner/file-scanner.d.ts +23 -0
- package/dist/scanner/file-scanner.d.ts.map +1 -1
- package/dist/scanner/file-scanner.js +86 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator/generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator/generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAeH;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1E;;GAEG;AACH,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,kBAAkB,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,OAAO,CAAC;CACtB;AAUD;;;;;;;GAOG;AACH,qBAAa,SAAS;IAMT,OAAO,CAAC,MAAM;IAL1B,OAAO,CAAC,SAAS,CAAqC;IACtD,OAAO,CAAC,UAAU,CAAwB;IAC1C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,iBAAiB,CAAuB;gBAE5B,MAAM,EAAE,eAAe;IAE3C;;;OAGG;IACG,GAAG,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBhD;;OAEG;YACW,YAAY;IA+B1B;;OAEG;YACW,eAAe;IAkB7B;;OAEG;YACW,QAAQ;IAuJtB;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;CAGvC"}
|
|
@@ -186,13 +186,18 @@ class Generator {
|
|
|
186
186
|
const linkingContent = (0, route_generator_1.generateLinkingFileContent)(routes, prefixes);
|
|
187
187
|
const registerContent = (0, route_generator_1.generateRegisterFileContent)();
|
|
188
188
|
const routeTreeContent = (0, route_generator_1.generateRouteTreeFileContent)(routes, routesDir, generatedDir);
|
|
189
|
+
const routeRegistryContent = (0, route_generator_1.generateRouteRegistryContent)(routes);
|
|
189
190
|
const manifestContent = {
|
|
190
191
|
generatedAt: new Date().toISOString(),
|
|
191
192
|
routeCount: routes.length,
|
|
192
193
|
routes: routes.map((r) => ({
|
|
193
194
|
path: r.path,
|
|
195
|
+
fullPath: r.fullPath,
|
|
196
|
+
id: r.id,
|
|
197
|
+
parentId: r.parentId,
|
|
194
198
|
file: r.relativePath,
|
|
195
199
|
params: r.params,
|
|
200
|
+
allParams: r.allParams,
|
|
196
201
|
layoutType: r.layoutType,
|
|
197
202
|
})),
|
|
198
203
|
};
|
|
@@ -202,6 +207,7 @@ class Generator {
|
|
|
202
207
|
linkingContent,
|
|
203
208
|
registerContent,
|
|
204
209
|
routeTreeContent,
|
|
210
|
+
routeRegistryContent,
|
|
205
211
|
JSON.stringify(manifestContent),
|
|
206
212
|
]);
|
|
207
213
|
// Skip writing if nothing changed
|
|
@@ -217,6 +223,7 @@ class Generator {
|
|
|
217
223
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "register.d.ts"), registerContent);
|
|
218
224
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "manifest.json"), JSON.stringify(manifestContent, null, 2));
|
|
219
225
|
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "routeTree.generated.ts"), routeTreeContent);
|
|
226
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "route-registry.generated.ts"), routeRegistryContent);
|
|
220
227
|
this.lastGeneratedHash = contentHash;
|
|
221
228
|
if (verbose) {
|
|
222
229
|
const count = routes.filter((r) => !r.isLayout).length;
|
|
@@ -24,6 +24,11 @@ export declare function generateLinkingFileContent(routes: RouteNode[], prefixes
|
|
|
24
24
|
* Generates the register.d.ts file content
|
|
25
25
|
*/
|
|
26
26
|
export declare function generateRegisterFileContent(): string;
|
|
27
|
+
/**
|
|
28
|
+
* Generates the route-registry.generated.ts file content
|
|
29
|
+
* This provides the FileRoutesByPath interface for type-safe routing
|
|
30
|
+
*/
|
|
31
|
+
export declare function generateRouteRegistryContent(routes: RouteNode[]): string;
|
|
27
32
|
export interface RouteTreeEntry {
|
|
28
33
|
importName: string;
|
|
29
34
|
importPath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-generator.d.ts","sourceRoot":"","sources":["../../src/generator/route-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAiB,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE9F,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,eAAe,EAAE,CAwBhF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAgDrE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAkC1F;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,
|
|
1
|
+
{"version":3,"file":"route-generator.d.ts","sourceRoot":"","sources":["../../src/generator/route-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAiB,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAE9F,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,eAAe,EAAE,CAwBhF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAgDrE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAkC1F;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAiBpD;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,CAuDxE;AAmFD,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC;AAED;;GAEG;AAEH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,cAAc,EAAE,CAqDpH;AAgLD;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAuFjH"}
|
|
@@ -8,6 +8,7 @@ exports.buildRouteParamsInterface = buildRouteParamsInterface;
|
|
|
8
8
|
exports.generateRoutesFileContent = generateRoutesFileContent;
|
|
9
9
|
exports.generateLinkingFileContent = generateLinkingFileContent;
|
|
10
10
|
exports.generateRegisterFileContent = generateRegisterFileContent;
|
|
11
|
+
exports.generateRouteRegistryContent = generateRouteRegistryContent;
|
|
11
12
|
exports.buildRouteTreeEntries = buildRouteTreeEntries;
|
|
12
13
|
exports.generateRouteTreeFileContent = generateRouteTreeFileContent;
|
|
13
14
|
const node_path_1 = require("node:path");
|
|
@@ -118,17 +119,83 @@ function generateRegisterFileContent() {
|
|
|
118
119
|
const lines = [
|
|
119
120
|
"// Auto-generated by @teardown/navigation",
|
|
120
121
|
'import type { RouteParams, RoutePath } from "./routes.generated";',
|
|
122
|
+
'import type { FileRoutesByPath } from "./route-registry.generated";',
|
|
121
123
|
"",
|
|
122
124
|
"declare module '@teardown/navigation' {",
|
|
123
125
|
"\tinterface Register {",
|
|
124
126
|
"\t\trouteParams: RouteParams;",
|
|
125
127
|
"\t\troutePath: RoutePath;",
|
|
128
|
+
"\t\tfileRoutesByPath: FileRoutesByPath;",
|
|
126
129
|
"\t}",
|
|
127
130
|
"}",
|
|
128
131
|
"",
|
|
129
132
|
];
|
|
130
133
|
return lines.join("\n");
|
|
131
134
|
}
|
|
135
|
+
/**
|
|
136
|
+
* Generates the route-registry.generated.ts file content
|
|
137
|
+
* This provides the FileRoutesByPath interface for type-safe routing
|
|
138
|
+
*/
|
|
139
|
+
function generateRouteRegistryContent(routes) {
|
|
140
|
+
const allRoutes = (0, file_scanner_1.flattenRoutes)(routes);
|
|
141
|
+
const screenRoutes = allRoutes.filter((r) => !r.isLayout);
|
|
142
|
+
const lines = [
|
|
143
|
+
"// Auto-generated by @teardown/navigation",
|
|
144
|
+
"// Do not edit this file directly",
|
|
145
|
+
`// Generated at: ${new Date().toISOString()}`,
|
|
146
|
+
"",
|
|
147
|
+
"/**",
|
|
148
|
+
" * FileRoutesByPath - Maps file paths to route metadata",
|
|
149
|
+
" * This interface is used for type-safe routing with scoped hooks",
|
|
150
|
+
" */",
|
|
151
|
+
"export interface FileRoutesByPath {",
|
|
152
|
+
];
|
|
153
|
+
for (const route of screenRoutes) {
|
|
154
|
+
const paramsType = generateParamsTypeForRegistry(route.params);
|
|
155
|
+
const allParamsType = generateParamsTypeForRegistry(route.allParams);
|
|
156
|
+
lines.push(` "${route.path}": {`);
|
|
157
|
+
lines.push(` id: "${route.id}";`);
|
|
158
|
+
lines.push(` path: "${route.path}";`);
|
|
159
|
+
lines.push(` fullPath: "${route.fullPath}";`);
|
|
160
|
+
lines.push(` parentId: ${route.parentId ? `"${route.parentId}"` : "null"};`);
|
|
161
|
+
lines.push(` params: ${paramsType};`);
|
|
162
|
+
lines.push(` allParams: ${allParamsType};`);
|
|
163
|
+
lines.push(` loaderData: unknown;`);
|
|
164
|
+
lines.push(` routeContext: unknown;`);
|
|
165
|
+
lines.push(` };`);
|
|
166
|
+
}
|
|
167
|
+
lines.push("}");
|
|
168
|
+
lines.push("");
|
|
169
|
+
// Add route metadata types
|
|
170
|
+
lines.push("/**");
|
|
171
|
+
lines.push(" * Union of all registered route paths");
|
|
172
|
+
lines.push(" */");
|
|
173
|
+
lines.push("export type RegisteredRoutePath = keyof FileRoutesByPath;");
|
|
174
|
+
lines.push("");
|
|
175
|
+
lines.push("/**");
|
|
176
|
+
lines.push(" * Get params for a specific route");
|
|
177
|
+
lines.push(" */");
|
|
178
|
+
lines.push("export type ParamsForRoute<T extends RegisteredRoutePath> = FileRoutesByPath[T]['allParams'];");
|
|
179
|
+
lines.push("");
|
|
180
|
+
lines.push("/**");
|
|
181
|
+
lines.push(" * Get route info for a specific route");
|
|
182
|
+
lines.push(" */");
|
|
183
|
+
lines.push("export type RouteInfo<T extends RegisteredRoutePath> = FileRoutesByPath[T];");
|
|
184
|
+
lines.push("");
|
|
185
|
+
return lines.join("\n");
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Generates params type for registry (handles empty params better)
|
|
189
|
+
*/
|
|
190
|
+
function generateParamsTypeForRegistry(params) {
|
|
191
|
+
if (params.length === 0)
|
|
192
|
+
return "Record<string, never>";
|
|
193
|
+
const entries = params.map((p) => {
|
|
194
|
+
const type = p.isCatchAll ? "string[]" : "string";
|
|
195
|
+
return `${p.name}${p.isOptional ? "?" : ""}: ${type}`;
|
|
196
|
+
});
|
|
197
|
+
return `{ ${entries.join("; ")} }`;
|
|
198
|
+
}
|
|
132
199
|
/**
|
|
133
200
|
* Generates the manifest.json content
|
|
134
201
|
*/
|
|
@@ -25,6 +25,16 @@ export interface RouteNode {
|
|
|
25
25
|
isCatchAll: boolean;
|
|
26
26
|
/** Route group name (from parentheses) */
|
|
27
27
|
groupName: string | null;
|
|
28
|
+
/** Unique route ID (derived from file path) */
|
|
29
|
+
id: string;
|
|
30
|
+
/** Full URL path from root (with route groups stripped) */
|
|
31
|
+
fullPath: string;
|
|
32
|
+
/** Parent route ID (null for root routes) */
|
|
33
|
+
parentId: string | null;
|
|
34
|
+
/** Array of child route IDs */
|
|
35
|
+
childIds: string[];
|
|
36
|
+
/** Accumulated params from all ancestors */
|
|
37
|
+
allParams: ParamDefinition[];
|
|
28
38
|
}
|
|
29
39
|
export interface ParamDefinition {
|
|
30
40
|
name: string;
|
|
@@ -63,4 +73,17 @@ export declare function filePathToScreenName(relativePath: string): string;
|
|
|
63
73
|
* Flattens a route tree into a flat array
|
|
64
74
|
*/
|
|
65
75
|
export declare function flattenRoutes(routes: RouteNode[]): RouteNode[];
|
|
76
|
+
/**
|
|
77
|
+
* Generate unique route ID from file path
|
|
78
|
+
*/
|
|
79
|
+
export declare function generateRouteId(relativePath: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* Compute full path by walking parent chain
|
|
82
|
+
* Strips route group segments like (auth)
|
|
83
|
+
*/
|
|
84
|
+
export declare function computeFullPath(node: RouteNode, nodeById: Map<string, RouteNode>): string;
|
|
85
|
+
/**
|
|
86
|
+
* Accumulate params from parent chain
|
|
87
|
+
*/
|
|
88
|
+
export declare function accumulateParams(node: RouteNode, nodeById: Map<string, RouteNode>): ParamDefinition[];
|
|
66
89
|
//# sourceMappingURL=file-scanner.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/file-scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,SAAS;IACzB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,mBAAmB;IACnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,sCAAsC;IACtC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,6BAA6B;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,4BAA4B;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,gCAAgC;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"file-scanner.d.ts","sourceRoot":"","sources":["../../src/scanner/file-scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,SAAS;IACzB,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,yBAAyB;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,+BAA+B;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,mBAAmB;IACnB,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,sCAAsC;IACtC,UAAU,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjD,6BAA6B;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,4BAA4B;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,gCAAgC;IAChC,UAAU,EAAE,OAAO,CAAC;IACpB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,+CAA+C;IAC/C,EAAE,EAAE,MAAM,CAAC;IACX,2DAA2D;IAC3D,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,4CAA4C;IAC5C,SAAS,EAAE,eAAe,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,CAuDjE;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAqCjE;AAgDD;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE,CAkBjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAmB/F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAGjE;AAwFD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAY9D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAc5D;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,MAAM,CAyBzF;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,eAAe,EAAE,CAkBrG"}
|
|
@@ -10,6 +10,9 @@ exports.extractParams = extractParams;
|
|
|
10
10
|
exports.buildUrlPath = buildUrlPath;
|
|
11
11
|
exports.filePathToScreenName = filePathToScreenName;
|
|
12
12
|
exports.flattenRoutes = flattenRoutes;
|
|
13
|
+
exports.generateRouteId = generateRouteId;
|
|
14
|
+
exports.computeFullPath = computeFullPath;
|
|
15
|
+
exports.accumulateParams = accumulateParams;
|
|
13
16
|
const node_fs_1 = require("node:fs");
|
|
14
17
|
const node_path_1 = require("node:path");
|
|
15
18
|
/**
|
|
@@ -25,15 +28,17 @@ function scanRoutesDirectory(routesDir) {
|
|
|
25
28
|
}
|
|
26
29
|
const files = findRouteFiles(routesDir);
|
|
27
30
|
const routeNodes = new Map();
|
|
31
|
+
const nodeById = new Map();
|
|
28
32
|
// First pass: create all route nodes
|
|
29
33
|
for (const file of files) {
|
|
30
34
|
const absolutePath = (0, node_path_1.join)(routesDir, file);
|
|
31
35
|
const node = parseRouteFile(file, absolutePath);
|
|
32
36
|
if (node) {
|
|
33
37
|
routeNodes.set(file, node);
|
|
38
|
+
nodeById.set(node.id, node);
|
|
34
39
|
}
|
|
35
40
|
}
|
|
36
|
-
// Second pass: build tree structure
|
|
41
|
+
// Second pass: build tree structure and set parentId
|
|
37
42
|
const rootNodes = [];
|
|
38
43
|
for (const [filePath, node] of routeNodes) {
|
|
39
44
|
const parentPath = findParentLayoutPath(filePath, routeNodes);
|
|
@@ -41,12 +46,22 @@ function scanRoutesDirectory(routesDir) {
|
|
|
41
46
|
const parent = routeNodes.get(parentPath);
|
|
42
47
|
if (parent) {
|
|
43
48
|
parent.children.push(node);
|
|
49
|
+
node.parentId = parent.id;
|
|
44
50
|
}
|
|
45
51
|
}
|
|
46
52
|
else {
|
|
47
53
|
rootNodes.push(node);
|
|
48
54
|
}
|
|
49
55
|
}
|
|
56
|
+
// Third pass: populate childIds, fullPath, and allParams
|
|
57
|
+
for (const node of routeNodes.values()) {
|
|
58
|
+
// Populate childIds
|
|
59
|
+
node.childIds = node.children.map((child) => child.id);
|
|
60
|
+
// Compute fullPath by walking parent chain
|
|
61
|
+
node.fullPath = computeFullPath(node, nodeById);
|
|
62
|
+
// Accumulate params from parent chain
|
|
63
|
+
node.allParams = accumulateParams(node, nodeById);
|
|
64
|
+
}
|
|
50
65
|
return { routes: rootNodes, errors };
|
|
51
66
|
}
|
|
52
67
|
/**
|
|
@@ -102,6 +117,8 @@ function parseRouteFile(relativePath, absolutePath) {
|
|
|
102
117
|
const params = extractParams(relativePath);
|
|
103
118
|
// Build URL path
|
|
104
119
|
const urlPath = buildUrlPath(relativePath, isIndex, isLayout);
|
|
120
|
+
// Generate unique ID
|
|
121
|
+
const id = generateRouteId(relativePath);
|
|
105
122
|
return {
|
|
106
123
|
name: filePathToScreenName(relativePath),
|
|
107
124
|
path: urlPath,
|
|
@@ -114,6 +131,12 @@ function parseRouteFile(relativePath, absolutePath) {
|
|
|
114
131
|
isLayout,
|
|
115
132
|
isCatchAll,
|
|
116
133
|
groupName,
|
|
134
|
+
// New fields - will be populated in second pass
|
|
135
|
+
id,
|
|
136
|
+
fullPath: "", // Computed after tree is built
|
|
137
|
+
parentId: null, // Set during tree building
|
|
138
|
+
childIds: [], // Populated after tree is built
|
|
139
|
+
allParams: [], // Computed after tree is built
|
|
117
140
|
};
|
|
118
141
|
}
|
|
119
142
|
/**
|
|
@@ -251,3 +274,65 @@ function flattenRoutes(routes) {
|
|
|
251
274
|
traverse(routes);
|
|
252
275
|
return result;
|
|
253
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Generate unique route ID from file path
|
|
279
|
+
*/
|
|
280
|
+
function generateRouteId(relativePath) {
|
|
281
|
+
return (relativePath
|
|
282
|
+
.replace(/\.(ts|tsx)$/, "")
|
|
283
|
+
.replace(/\//g, "_")
|
|
284
|
+
.replace(/\(([^)]+)\)/g, "") // Strip route groups
|
|
285
|
+
.replace(/\[\.\.\.([^\]]+)\]/g, "CatchAll_$1")
|
|
286
|
+
.replace(/\[\[([^\]]+)\]\]/g, "Optional_$1")
|
|
287
|
+
.replace(/\[([^\]]+)\]/g, "Param_$1")
|
|
288
|
+
.replace(/[^a-zA-Z0-9_]/g, "_")
|
|
289
|
+
.replace(/^_+/, "")
|
|
290
|
+
.replace(/_+$/g, "")
|
|
291
|
+
.replace(/_+/g, "_") || "root");
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Compute full path by walking parent chain
|
|
295
|
+
* Strips route group segments like (auth)
|
|
296
|
+
*/
|
|
297
|
+
function computeFullPath(node, nodeById) {
|
|
298
|
+
if (node.isLayout) {
|
|
299
|
+
// Layouts contribute their path segment but don't have their own "full path" as a destination
|
|
300
|
+
// We still compute it for internal use
|
|
301
|
+
}
|
|
302
|
+
const segments = [];
|
|
303
|
+
let current = node;
|
|
304
|
+
while (current) {
|
|
305
|
+
// Strip route groups from segment and clean up
|
|
306
|
+
let segment = current.path.replace(/\(([^)]+)\)\/?/g, "");
|
|
307
|
+
// Remove leading slash for non-root segments
|
|
308
|
+
segment = segment.replace(/^\//, "");
|
|
309
|
+
if (segment && segment !== "/" && segment !== "") {
|
|
310
|
+
segments.unshift(segment);
|
|
311
|
+
}
|
|
312
|
+
current = current.parentId ? nodeById.get(current.parentId) : undefined;
|
|
313
|
+
}
|
|
314
|
+
const fullPath = "/" + segments.join("/");
|
|
315
|
+
// Clean up double slashes and trailing slashes
|
|
316
|
+
return fullPath.replace(/\/+/g, "/").replace(/\/$/, "") || "/";
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Accumulate params from parent chain
|
|
320
|
+
*/
|
|
321
|
+
function accumulateParams(node, nodeById) {
|
|
322
|
+
const allParams = [];
|
|
323
|
+
let current = node;
|
|
324
|
+
// Collect params from current node up to root
|
|
325
|
+
while (current) {
|
|
326
|
+
// Prepend parent params so they appear first
|
|
327
|
+
allParams.unshift(...current.params);
|
|
328
|
+
current = current.parentId ? nodeById.get(current.parentId) : undefined;
|
|
329
|
+
}
|
|
330
|
+
// Remove duplicates by name (keep first occurrence)
|
|
331
|
+
const seen = new Set();
|
|
332
|
+
return allParams.filter((p) => {
|
|
333
|
+
if (seen.has(p.name))
|
|
334
|
+
return false;
|
|
335
|
+
seen.add(p.name);
|
|
336
|
+
return true;
|
|
337
|
+
});
|
|
338
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teardown/navigation-metro",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.82",
|
|
4
4
|
"description": "Metro plugin for @teardown/navigation type generation",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@biomejs/biome": "2.3.11",
|
|
45
|
-
"@teardown/tsconfig": "2.0.
|
|
45
|
+
"@teardown/tsconfig": "2.0.82",
|
|
46
46
|
"@types/node": "24.10.1",
|
|
47
47
|
"typescript": "5.9.3"
|
|
48
48
|
}
|