@teardown/navigation-metro 2.0.53 → 2.0.56

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.
Files changed (38) hide show
  1. package/dist/generator/index.d.ts +5 -0
  2. package/dist/generator/index.d.ts.map +1 -0
  3. package/dist/generator/index.js +12 -0
  4. package/dist/generator/route-generator.d.ts +37 -0
  5. package/dist/generator/route-generator.d.ts.map +1 -0
  6. package/dist/generator/route-generator.js +179 -0
  7. package/dist/index.d.ts +83 -0
  8. package/dist/index.d.ts.map +1 -0
  9. package/dist/index.js +103 -0
  10. package/dist/scanner/file-scanner.d.ts +62 -0
  11. package/dist/scanner/file-scanner.d.ts.map +1 -0
  12. package/dist/scanner/file-scanner.js +250 -0
  13. package/dist/scanner/index.d.ts +5 -0
  14. package/dist/scanner/index.d.ts.map +1 -0
  15. package/dist/scanner/index.js +12 -0
  16. package/{src/validator/index.ts → dist/validator/index.d.ts} +1 -1
  17. package/dist/validator/index.d.ts.map +1 -0
  18. package/dist/validator/index.js +8 -0
  19. package/dist/validator/route-validator.d.ts +15 -0
  20. package/dist/validator/route-validator.d.ts.map +1 -0
  21. package/dist/validator/route-validator.js +153 -0
  22. package/dist/watcher/file-watcher.d.ts +27 -0
  23. package/dist/watcher/file-watcher.d.ts.map +1 -0
  24. package/dist/watcher/file-watcher.js +110 -0
  25. package/{src/watcher/index.ts → dist/watcher/index.d.ts} +1 -1
  26. package/dist/watcher/index.d.ts.map +1 -0
  27. package/dist/watcher/index.js +10 -0
  28. package/package.json +12 -9
  29. package/src/generator/index.ts +0 -13
  30. package/src/generator/route-generator.test.ts +0 -287
  31. package/src/generator/route-generator.ts +0 -231
  32. package/src/index.ts +0 -158
  33. package/src/scanner/file-scanner.test.ts +0 -271
  34. package/src/scanner/file-scanner.ts +0 -329
  35. package/src/scanner/index.ts +0 -15
  36. package/src/validator/route-validator.test.ts +0 -192
  37. package/src/validator/route-validator.ts +0 -178
  38. package/src/watcher/file-watcher.ts +0 -132
@@ -1,13 +0,0 @@
1
- /**
2
- * Generator module for @teardown/navigation-metro
3
- */
4
-
5
- export {
6
- buildRouteParamsInterface,
7
- type GenerateOptions,
8
- generateAllRouteFiles,
9
- generateLinkingFileContent,
10
- generateRegisterFileContent,
11
- generateRoutesFileContent,
12
- type RouteParamEntry,
13
- } from "./route-generator";
@@ -1,287 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it } from "bun:test";
2
- import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import { scanRoutesDirectory } from "../scanner/file-scanner";
5
- import {
6
- buildRouteParamsInterface,
7
- generateAllRouteFiles,
8
- generateLinkingFileContent,
9
- generateRegisterFileContent,
10
- generateRoutesFileContent,
11
- } from "./route-generator";
12
-
13
- const TEST_ROUTES_DIR = join(import.meta.dir, "__test_routes__");
14
- const TEST_GENERATED_DIR = join(import.meta.dir, "__test_generated__");
15
-
16
- function createTestFile(relativePath: string, content = "export default {}") {
17
- const fullPath = join(TEST_ROUTES_DIR, relativePath);
18
- const dir = fullPath.substring(0, fullPath.lastIndexOf("/"));
19
- mkdirSync(dir, { recursive: true });
20
- writeFileSync(fullPath, content);
21
- }
22
-
23
- describe("Route Generator", () => {
24
- beforeEach(() => {
25
- mkdirSync(TEST_ROUTES_DIR, { recursive: true });
26
- mkdirSync(TEST_GENERATED_DIR, { recursive: true });
27
- });
28
-
29
- afterEach(() => {
30
- rmSync(TEST_ROUTES_DIR, { recursive: true, force: true });
31
- rmSync(TEST_GENERATED_DIR, { recursive: true, force: true });
32
- });
33
-
34
- describe("buildRouteParamsInterface", () => {
35
- it("should return empty array for empty routes", () => {
36
- const result = buildRouteParamsInterface([]);
37
- expect(result).toEqual([]);
38
- });
39
-
40
- it("should generate static route with undefined params", () => {
41
- createTestFile("about.tsx");
42
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
43
-
44
- const result = buildRouteParamsInterface(routes);
45
- expect(result).toEqual([{ path: "/about", params: [] }]);
46
- });
47
-
48
- it("should generate dynamic route with params", () => {
49
- createTestFile("users/[userId].tsx");
50
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
51
-
52
- const result = buildRouteParamsInterface(routes);
53
- const userRoute = result.find((r) => r.path === "/users/:userId");
54
- expect(userRoute).toBeDefined();
55
- expect(userRoute?.params).toEqual([{ name: "userId", isOptional: false, isCatchAll: false }]);
56
- });
57
-
58
- it("should handle optional params", () => {
59
- createTestFile("settings/[section]?.tsx");
60
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
61
-
62
- const result = buildRouteParamsInterface(routes);
63
- const settingsRoute = result.find((r) => r.path === "/settings/:section?");
64
- expect(settingsRoute).toBeDefined();
65
- expect(settingsRoute?.params).toEqual([{ name: "section", isOptional: true, isCatchAll: false }]);
66
- });
67
-
68
- it("should handle catch-all params", () => {
69
- createTestFile("[...slug].tsx");
70
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
71
-
72
- const result = buildRouteParamsInterface(routes);
73
- const catchAllRoute = result.find((r) => r.path === "/*");
74
- expect(catchAllRoute).toBeDefined();
75
- expect(catchAllRoute?.params).toEqual([{ name: "slug", isOptional: false, isCatchAll: true }]);
76
- });
77
-
78
- it("should accumulate params from parent layouts", () => {
79
- createTestFile("users/_layout.tsx");
80
- createTestFile("users/[userId]/_layout.tsx");
81
- createTestFile("users/[userId]/posts/[postId].tsx");
82
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
83
-
84
- const result = buildRouteParamsInterface(routes);
85
- const postRoute = result.find((r) => r.path === "/users/:userId/posts/:postId");
86
- expect(postRoute).toBeDefined();
87
- expect(postRoute?.params.length).toBe(2);
88
- expect(postRoute?.params.map((p) => p.name)).toContain("userId");
89
- expect(postRoute?.params.map((p) => p.name)).toContain("postId");
90
- });
91
-
92
- it("should skip layout files from route entries", () => {
93
- createTestFile("_layout.tsx");
94
- createTestFile("index.tsx");
95
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
96
-
97
- const result = buildRouteParamsInterface(routes);
98
- // Should only have index, not layout
99
- expect(result.length).toBe(1);
100
- expect(result[0].path).toBe("/");
101
- });
102
- });
103
-
104
- describe("generateRoutesFileContent", () => {
105
- it("should generate valid TypeScript for static routes", () => {
106
- createTestFile("index.tsx");
107
- createTestFile("about.tsx");
108
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
109
-
110
- const content = generateRoutesFileContent(routes);
111
-
112
- expect(content).toContain("export interface RouteParams");
113
- expect(content).toContain('"/": undefined');
114
- expect(content).toContain('"/about": undefined');
115
- expect(content).toContain("export type RoutePath = keyof RouteParams");
116
- expect(content).toContain("export type ParamsFor<T extends RoutePath> = RouteParams[T]");
117
- });
118
-
119
- it("should generate valid TypeScript for dynamic routes", () => {
120
- createTestFile("users/[userId].tsx");
121
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
122
-
123
- const content = generateRoutesFileContent(routes);
124
-
125
- expect(content).toContain('"/users/:userId": { userId: string }');
126
- });
127
-
128
- it("should generate valid TypeScript for optional params", () => {
129
- createTestFile("settings/[section]?.tsx");
130
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
131
-
132
- const content = generateRoutesFileContent(routes);
133
-
134
- expect(content).toContain('"/settings/:section?": { section?: string }');
135
- });
136
-
137
- it("should generate valid TypeScript for catch-all routes", () => {
138
- createTestFile("[...slug].tsx");
139
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
140
-
141
- const content = generateRoutesFileContent(routes);
142
-
143
- expect(content).toContain('"/*": { slug: string[] }');
144
- });
145
-
146
- it("should include RouteWithParams utility type", () => {
147
- createTestFile("index.tsx");
148
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
149
-
150
- const content = generateRoutesFileContent(routes);
151
-
152
- expect(content).toContain("export type RouteWithParams");
153
- });
154
-
155
- it("should include NavigatorType", () => {
156
- createTestFile("index.tsx");
157
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
158
-
159
- const content = generateRoutesFileContent(routes);
160
-
161
- expect(content).toContain('export type NavigatorType = "stack" | "tabs" | "drawer"');
162
- });
163
- });
164
-
165
- describe("generateLinkingFileContent", () => {
166
- it("should generate linking config for static routes", () => {
167
- createTestFile("index.tsx");
168
- createTestFile("about.tsx");
169
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
170
-
171
- const content = generateLinkingFileContent(routes, ["myapp://"]);
172
-
173
- expect(content).toContain("export const generatedLinkingConfig");
174
- expect(content).toContain('"myapp://"');
175
- });
176
-
177
- it("should convert dynamic params to react-navigation format", () => {
178
- createTestFile("users/[userId].tsx");
179
- const { routes } = scanRoutesDirectory(TEST_ROUTES_DIR);
180
-
181
- const content = generateLinkingFileContent(routes, []);
182
-
183
- // Should have :userId format for react-navigation
184
- expect(content).toContain('"/users/:userId"');
185
- });
186
- });
187
-
188
- describe("generateRegisterFileContent", () => {
189
- it("should generate module augmentation", () => {
190
- const content = generateRegisterFileContent();
191
-
192
- expect(content).toContain("declare module '@teardown/navigation'");
193
- expect(content).toContain("interface Register");
194
- expect(content).toContain("routeParams: RouteParams");
195
- expect(content).toContain("routePath: RoutePath");
196
- });
197
- });
198
-
199
- describe("generateAllRouteFiles", () => {
200
- it("should create generated directory if not exists", () => {
201
- rmSync(TEST_GENERATED_DIR, { recursive: true, force: true });
202
- createTestFile("index.tsx");
203
-
204
- generateAllRouteFiles({
205
- routesDir: TEST_ROUTES_DIR,
206
- generatedDir: TEST_GENERATED_DIR,
207
- prefixes: [],
208
- verbose: false,
209
- });
210
-
211
- expect(existsSync(TEST_GENERATED_DIR)).toBe(true);
212
- });
213
-
214
- it("should generate routes.generated.ts", () => {
215
- createTestFile("index.tsx");
216
- createTestFile("about.tsx");
217
-
218
- generateAllRouteFiles({
219
- routesDir: TEST_ROUTES_DIR,
220
- generatedDir: TEST_GENERATED_DIR,
221
- prefixes: [],
222
- verbose: false,
223
- });
224
-
225
- const generatedPath = join(TEST_GENERATED_DIR, "routes.generated.ts");
226
- expect(existsSync(generatedPath)).toBe(true);
227
-
228
- const content = readFileSync(generatedPath, "utf-8");
229
- expect(content).toContain("RouteParams");
230
- expect(content).toContain('"/about"');
231
- });
232
-
233
- it("should generate linking.generated.ts", () => {
234
- createTestFile("index.tsx");
235
-
236
- generateAllRouteFiles({
237
- routesDir: TEST_ROUTES_DIR,
238
- generatedDir: TEST_GENERATED_DIR,
239
- prefixes: ["myapp://"],
240
- verbose: false,
241
- });
242
-
243
- const generatedPath = join(TEST_GENERATED_DIR, "linking.generated.ts");
244
- expect(existsSync(generatedPath)).toBe(true);
245
-
246
- const content = readFileSync(generatedPath, "utf-8");
247
- expect(content).toContain("generatedLinkingConfig");
248
- });
249
-
250
- it("should generate register.d.ts", () => {
251
- createTestFile("index.tsx");
252
-
253
- generateAllRouteFiles({
254
- routesDir: TEST_ROUTES_DIR,
255
- generatedDir: TEST_GENERATED_DIR,
256
- prefixes: [],
257
- verbose: false,
258
- });
259
-
260
- const generatedPath = join(TEST_GENERATED_DIR, "register.d.ts");
261
- expect(existsSync(generatedPath)).toBe(true);
262
-
263
- const content = readFileSync(generatedPath, "utf-8");
264
- expect(content).toContain("declare module '@teardown/navigation'");
265
- });
266
-
267
- it("should generate manifest.json", () => {
268
- createTestFile("index.tsx");
269
- createTestFile("users/[userId].tsx");
270
-
271
- generateAllRouteFiles({
272
- routesDir: TEST_ROUTES_DIR,
273
- generatedDir: TEST_GENERATED_DIR,
274
- prefixes: [],
275
- verbose: false,
276
- });
277
-
278
- const generatedPath = join(TEST_GENERATED_DIR, "manifest.json");
279
- expect(existsSync(generatedPath)).toBe(true);
280
-
281
- const content = JSON.parse(readFileSync(generatedPath, "utf-8"));
282
- expect(content.routeCount).toBe(2);
283
- expect(content.routes).toBeInstanceOf(Array);
284
- expect(content.generatedAt).toBeDefined();
285
- });
286
- });
287
- });
@@ -1,231 +0,0 @@
1
- /**
2
- * Route generator for @teardown/navigation-metro
3
- * Generates TypeScript type definitions from scanned route tree
4
- */
5
-
6
- import { mkdirSync, writeFileSync } from "node:fs";
7
- import { join } from "node:path";
8
- import { flattenRoutes, type ParamDefinition, type RouteNode, scanRoutesDirectory } from "../scanner/file-scanner";
9
-
10
- export interface GenerateOptions {
11
- routesDir: string;
12
- generatedDir: string;
13
- prefixes: string[];
14
- verbose: boolean;
15
- }
16
-
17
- export interface RouteParamEntry {
18
- path: string;
19
- params: ParamDefinition[];
20
- }
21
-
22
- /**
23
- * Generates all route files (routes.generated.ts, linking.generated.ts, register.d.ts, manifest.json)
24
- */
25
- export function generateAllRouteFiles(options: GenerateOptions): void {
26
- const { routesDir, generatedDir, prefixes, verbose } = options;
27
-
28
- // Ensure generated directory exists
29
- mkdirSync(generatedDir, { recursive: true });
30
-
31
- // Scan routes
32
- const { routes, errors } = scanRoutesDirectory(routesDir);
33
-
34
- if (errors.length > 0 && verbose) {
35
- console.warn("[teardown/navigation] Scan warnings:", errors);
36
- }
37
-
38
- // Generate files
39
- const routesContent = generateRoutesFileContent(routes);
40
- writeFileSync(join(generatedDir, "routes.generated.ts"), routesContent);
41
-
42
- const linkingContent = generateLinkingFileContent(routes, prefixes);
43
- writeFileSync(join(generatedDir, "linking.generated.ts"), linkingContent);
44
-
45
- const registerContent = generateRegisterFileContent();
46
- writeFileSync(join(generatedDir, "register.d.ts"), registerContent);
47
-
48
- const manifestContent = generateManifestContent(routes);
49
- writeFileSync(join(generatedDir, "manifest.json"), JSON.stringify(manifestContent, null, 2));
50
-
51
- if (verbose) {
52
- const count = countRoutes(routes);
53
- console.log(`[teardown/navigation] Generated ${count} routes`);
54
- }
55
- }
56
-
57
- /**
58
- * Builds the RouteParams interface entries from the route tree
59
- * Each route has all its params extracted from its full path, so no accumulation needed
60
- */
61
- export function buildRouteParamsInterface(routes: RouteNode[]): RouteParamEntry[] {
62
- const result: RouteParamEntry[] = [];
63
-
64
- function traverse(nodes: RouteNode[]): void {
65
- for (const node of nodes) {
66
- if (node.isLayout) {
67
- // Layouts don't get their own route entry
68
- traverse(node.children);
69
- continue;
70
- }
71
-
72
- result.push({
73
- path: node.path,
74
- params: node.params,
75
- });
76
-
77
- if (node.children.length > 0) {
78
- traverse(node.children);
79
- }
80
- }
81
- }
82
-
83
- traverse(routes);
84
- return result;
85
- }
86
-
87
- /**
88
- * Generates the routes.generated.ts file content
89
- */
90
- export function generateRoutesFileContent(routes: RouteNode[]): string {
91
- const routeParams = buildRouteParamsInterface(routes);
92
-
93
- const lines: string[] = [
94
- "// Auto-generated by @teardown/navigation",
95
- "// Do not edit this file directly",
96
- `// Generated at: ${new Date().toISOString()}`,
97
- "",
98
- "export interface RouteParams {",
99
- ];
100
-
101
- // Add route param entries
102
- for (const { path, params } of routeParams) {
103
- const paramsType =
104
- params.length === 0
105
- ? "undefined"
106
- : `{ ${params
107
- .map((p) => {
108
- const type = p.isCatchAll ? "string[]" : "string";
109
- return `${p.name}${p.isOptional ? "?" : ""}: ${type}`;
110
- })
111
- .join("; ")} }`;
112
-
113
- lines.push(`\t"${path}": ${paramsType};`);
114
- }
115
-
116
- lines.push("}");
117
- lines.push("");
118
-
119
- // Add type aliases
120
- lines.push("export type RoutePath = keyof RouteParams;");
121
- lines.push("");
122
- lines.push("export type ParamsFor<T extends RoutePath> = RouteParams[T];");
123
- lines.push("");
124
-
125
- // Add RouteWithParams utility type
126
- lines.push("export type RouteWithParams = {");
127
- lines.push("\t[K in RoutePath]: RouteParams[K] extends undefined");
128
- lines.push("\t\t? { path: K }");
129
- lines.push("\t\t: { path: K; params: RouteParams[K] };");
130
- lines.push("}[RoutePath];");
131
- lines.push("");
132
-
133
- // Add NavigatorType
134
- lines.push('export type NavigatorType = "stack" | "tabs" | "drawer";');
135
- lines.push("");
136
-
137
- return lines.join("\n");
138
- }
139
-
140
- /**
141
- * Generates the linking.generated.ts file content
142
- */
143
- export function generateLinkingFileContent(routes: RouteNode[], prefixes: string[]): string {
144
- const routeParams = buildRouteParamsInterface(routes);
145
-
146
- const lines: string[] = [
147
- "// Auto-generated by @teardown/navigation",
148
- 'import type { LinkingOptions } from "@react-navigation/native";',
149
- 'import type { RouteParams } from "./routes.generated";',
150
- "",
151
- ];
152
-
153
- // Build screens config
154
- const screens: Record<string, string> = {};
155
- for (const { path } of routeParams) {
156
- // Convert /users/:userId to users/:userId (remove leading slash)
157
- const linkingPath = path === "/" ? "" : path.slice(1);
158
- screens[path] = linkingPath;
159
- }
160
-
161
- lines.push('export const generatedLinkingConfig: LinkingOptions<RouteParams>["config"] = {');
162
- lines.push("\tscreens: {");
163
-
164
- for (const [routePath, linkingPath] of Object.entries(screens)) {
165
- lines.push(`\t\t"${routePath}": "${linkingPath}",`);
166
- }
167
-
168
- lines.push("\t},");
169
- lines.push("};");
170
- lines.push("");
171
-
172
- // Add prefixes
173
- lines.push(`export const defaultPrefixes: string[] = ${JSON.stringify(prefixes)};`);
174
- lines.push("");
175
-
176
- return lines.join("\n");
177
- }
178
-
179
- /**
180
- * Generates the register.d.ts file content
181
- */
182
- export function generateRegisterFileContent(): string {
183
- const lines: string[] = [
184
- "// Auto-generated by @teardown/navigation",
185
- 'import type { RouteParams, RoutePath } from "./routes.generated";',
186
- "",
187
- "declare module '@teardown/navigation' {",
188
- "\tinterface Register {",
189
- "\t\trouteParams: RouteParams;",
190
- "\t\troutePath: RoutePath;",
191
- "\t}",
192
- "}",
193
- "",
194
- ];
195
-
196
- return lines.join("\n");
197
- }
198
-
199
- /**
200
- * Generates the manifest.json content
201
- */
202
- function generateManifestContent(routes: RouteNode[]): {
203
- generatedAt: string;
204
- routeCount: number;
205
- routes: Array<{
206
- path: string;
207
- file: string;
208
- params: ParamDefinition[];
209
- layoutType: string;
210
- }>;
211
- } {
212
- const allRoutes = flattenRoutes(routes);
213
-
214
- return {
215
- generatedAt: new Date().toISOString(),
216
- routeCount: allRoutes.filter((r) => !r.isLayout).length,
217
- routes: allRoutes.map((r) => ({
218
- path: r.path,
219
- file: r.relativePath,
220
- params: r.params,
221
- layoutType: r.layoutType,
222
- })),
223
- };
224
- }
225
-
226
- /**
227
- * Counts the number of non-layout routes
228
- */
229
- function countRoutes(routes: RouteNode[]): number {
230
- return flattenRoutes(routes).filter((r) => !r.isLayout).length;
231
- }
package/src/index.ts DELETED
@@ -1,158 +0,0 @@
1
- /**
2
- * @teardown/navigation-metro
3
- * Metro plugin for type-safe file-based navigation
4
- */
5
-
6
- import { resolve } from "node:path";
7
- import { generateAllRouteFiles } from "./generator/route-generator";
8
- import { startRouteWatcher } from "./watcher/file-watcher";
9
-
10
- /**
11
- * Options for the Teardown Navigation Metro plugin
12
- */
13
- export interface TeardownNavigationOptions {
14
- /**
15
- * Path to routes directory relative to project root
16
- * @default './src/routes'
17
- */
18
- routesDir?: string;
19
-
20
- /**
21
- * Path for generated type files
22
- * @default './.teardown'
23
- */
24
- generatedDir?: string;
25
-
26
- /**
27
- * Deep link URL prefixes
28
- * @default []
29
- */
30
- prefixes?: string[];
31
-
32
- /**
33
- * Enable verbose logging
34
- * @default false
35
- */
36
- verbose?: boolean;
37
- }
38
-
39
- /**
40
- * Metro configuration type (simplified)
41
- * Full type from metro-config can be used if available
42
- */
43
- export interface MetroConfig {
44
- projectRoot?: string;
45
- watchFolders?: string[];
46
- resolver?: {
47
- resolveRequest?: (
48
- context: unknown,
49
- moduleName: string,
50
- platform: string | null
51
- ) => { filePath: string; type: string } | null;
52
- [key: string]: unknown;
53
- };
54
- transformer?: {
55
- unstable_allowRequireContext?: boolean;
56
- [key: string]: unknown;
57
- };
58
- [key: string]: unknown;
59
- }
60
-
61
- /**
62
- * Wraps a Metro configuration with Teardown Navigation support
63
- *
64
- * This function:
65
- * 1. Generates TypeScript type definitions on startup
66
- * 2. Watches for route file changes in development
67
- * 3. Configures Metro to include generated files
68
- *
69
- * @example
70
- * ```js
71
- * // metro.config.js
72
- * const { getDefaultConfig } = require('expo/metro-config');
73
- * const { withTeardownNavigation } = require('@teardown/navigation-metro');
74
- *
75
- * const config = getDefaultConfig(__dirname);
76
- *
77
- * module.exports = withTeardownNavigation(config, {
78
- * routesDir: './src/routes',
79
- * generatedDir: './.teardown',
80
- * prefixes: ['myapp://', 'https://myapp.com'],
81
- * verbose: true,
82
- * });
83
- * ```
84
- */
85
- export function withTeardownNavigation(config: MetroConfig, options: TeardownNavigationOptions = {}): MetroConfig {
86
- const { routesDir = "./src/routes", generatedDir = "./.teardown", prefixes = [], verbose = false } = options;
87
-
88
- const projectRoot = config.projectRoot ?? process.cwd();
89
- const absoluteRoutesDir = resolve(projectRoot, routesDir);
90
- const absoluteGeneratedDir = resolve(projectRoot, generatedDir);
91
-
92
- // Generate types on startup
93
- try {
94
- generateAllRouteFiles({
95
- routesDir: absoluteRoutesDir,
96
- generatedDir: absoluteGeneratedDir,
97
- prefixes,
98
- verbose,
99
- });
100
-
101
- if (verbose) {
102
- console.log("[teardown/navigation] Initial generation complete");
103
- }
104
- } catch (error) {
105
- console.error("[teardown/navigation] Initial generation failed:", error);
106
- }
107
-
108
- // Start file watcher in development
109
- if (process.env.NODE_ENV !== "production") {
110
- startRouteWatcher({
111
- routesDir: absoluteRoutesDir,
112
- generatedDir: absoluteGeneratedDir,
113
- prefixes,
114
- verbose,
115
- onRegenerate: () => {
116
- if (verbose) {
117
- console.log("[teardown/navigation] Routes regenerated");
118
- }
119
- },
120
- onError: (_errors) => {
121
- if (verbose) {
122
- console.error("[teardown/navigation] Validation errors during watch");
123
- }
124
- },
125
- });
126
- }
127
-
128
- // Add watch folders for Metro
129
- const watchFolders = [...(config.watchFolders ?? []), absoluteRoutesDir, absoluteGeneratedDir];
130
-
131
- return {
132
- ...config,
133
- watchFolders,
134
- transformer: {
135
- ...config.transformer,
136
- unstable_allowRequireContext: true,
137
- },
138
- resolver: {
139
- ...config.resolver,
140
- },
141
- };
142
- }
143
-
144
- export type { GenerateOptions, RouteParamEntry } from "./generator/route-generator";
145
- // Re-export modules
146
- export { generateAllRouteFiles } from "./generator/route-generator";
147
- export type { ParamDefinition, RouteNode, ScanError, ScanResult } from "./scanner/file-scanner";
148
- export {
149
- buildUrlPath,
150
- extractParams,
151
- filePathToScreenName,
152
- flattenRoutes,
153
- scanRoutesDirectory,
154
- } from "./scanner/file-scanner";
155
- export type { ValidationError } from "./validator/route-validator";
156
- export { validateRoutes } from "./validator/route-validator";
157
- export type { WatcherOptions } from "./watcher/file-watcher";
158
- export { isWatcherRunning, startRouteWatcher, stopRouteWatcher } from "./watcher/file-watcher";