@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.
- package/dist/generator/index.d.ts +5 -0
- package/dist/generator/index.d.ts.map +1 -0
- package/dist/generator/index.js +12 -0
- package/dist/generator/route-generator.d.ts +37 -0
- package/dist/generator/route-generator.d.ts.map +1 -0
- package/dist/generator/route-generator.js +179 -0
- package/dist/index.d.ts +83 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +103 -0
- package/dist/scanner/file-scanner.d.ts +62 -0
- package/dist/scanner/file-scanner.d.ts.map +1 -0
- package/dist/scanner/file-scanner.js +250 -0
- package/dist/scanner/index.d.ts +5 -0
- package/dist/scanner/index.d.ts.map +1 -0
- package/dist/scanner/index.js +12 -0
- package/{src/validator/index.ts → dist/validator/index.d.ts} +1 -1
- package/dist/validator/index.d.ts.map +1 -0
- package/dist/validator/index.js +8 -0
- package/dist/validator/route-validator.d.ts +15 -0
- package/dist/validator/route-validator.d.ts.map +1 -0
- package/dist/validator/route-validator.js +153 -0
- package/dist/watcher/file-watcher.d.ts +27 -0
- package/dist/watcher/file-watcher.d.ts.map +1 -0
- package/dist/watcher/file-watcher.js +110 -0
- package/{src/watcher/index.ts → dist/watcher/index.d.ts} +1 -1
- package/dist/watcher/index.d.ts.map +1 -0
- package/dist/watcher/index.js +10 -0
- package/package.json +12 -9
- package/src/generator/index.ts +0 -13
- package/src/generator/route-generator.test.ts +0 -287
- package/src/generator/route-generator.ts +0 -231
- package/src/index.ts +0 -158
- package/src/scanner/file-scanner.test.ts +0 -271
- package/src/scanner/file-scanner.ts +0 -329
- package/src/scanner/index.ts +0 -15
- package/src/validator/route-validator.test.ts +0 -192
- package/src/validator/route-validator.ts +0 -178
- package/src/watcher/file-watcher.ts +0 -132
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator module for @teardown/navigation-metro
|
|
3
|
+
*/
|
|
4
|
+
export { buildRouteParamsInterface, type GenerateOptions, generateAllRouteFiles, generateLinkingFileContent, generateRegisterFileContent, generateRoutesFileContent, type RouteParamEntry, } from "./route-generator";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,yBAAyB,EACzB,KAAK,eAAe,EACpB,qBAAqB,EACrB,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,EACzB,KAAK,eAAe,GACpB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generator module for @teardown/navigation-metro
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateRoutesFileContent = exports.generateRegisterFileContent = exports.generateLinkingFileContent = exports.generateAllRouteFiles = exports.buildRouteParamsInterface = void 0;
|
|
7
|
+
var route_generator_1 = require("./route-generator");
|
|
8
|
+
Object.defineProperty(exports, "buildRouteParamsInterface", { enumerable: true, get: function () { return route_generator_1.buildRouteParamsInterface; } });
|
|
9
|
+
Object.defineProperty(exports, "generateAllRouteFiles", { enumerable: true, get: function () { return route_generator_1.generateAllRouteFiles; } });
|
|
10
|
+
Object.defineProperty(exports, "generateLinkingFileContent", { enumerable: true, get: function () { return route_generator_1.generateLinkingFileContent; } });
|
|
11
|
+
Object.defineProperty(exports, "generateRegisterFileContent", { enumerable: true, get: function () { return route_generator_1.generateRegisterFileContent; } });
|
|
12
|
+
Object.defineProperty(exports, "generateRoutesFileContent", { enumerable: true, get: function () { return route_generator_1.generateRoutesFileContent; } });
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route generator for @teardown/navigation-metro
|
|
3
|
+
* Generates TypeScript type definitions from scanned route tree
|
|
4
|
+
*/
|
|
5
|
+
import { type ParamDefinition, type RouteNode } from "../scanner/file-scanner";
|
|
6
|
+
export interface GenerateOptions {
|
|
7
|
+
routesDir: string;
|
|
8
|
+
generatedDir: string;
|
|
9
|
+
prefixes: string[];
|
|
10
|
+
verbose: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface RouteParamEntry {
|
|
13
|
+
path: string;
|
|
14
|
+
params: ParamDefinition[];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Generates all route files (routes.generated.ts, linking.generated.ts, register.d.ts, manifest.json)
|
|
18
|
+
*/
|
|
19
|
+
export declare function generateAllRouteFiles(options: GenerateOptions): void;
|
|
20
|
+
/**
|
|
21
|
+
* Builds the RouteParams interface entries from the route tree
|
|
22
|
+
* Each route has all its params extracted from its full path, so no accumulation needed
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildRouteParamsInterface(routes: RouteNode[]): RouteParamEntry[];
|
|
25
|
+
/**
|
|
26
|
+
* Generates the routes.generated.ts file content
|
|
27
|
+
*/
|
|
28
|
+
export declare function generateRoutesFileContent(routes: RouteNode[]): string;
|
|
29
|
+
/**
|
|
30
|
+
* Generates the linking.generated.ts file content
|
|
31
|
+
*/
|
|
32
|
+
export declare function generateLinkingFileContent(routes: RouteNode[], prefixes: string[]): string;
|
|
33
|
+
/**
|
|
34
|
+
* Generates the register.d.ts file content
|
|
35
|
+
*/
|
|
36
|
+
export declare function generateRegisterFileContent(): string;
|
|
37
|
+
//# sourceMappingURL=route-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"route-generator.d.ts","sourceRoot":"","sources":["../../src/generator/route-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAiB,KAAK,eAAe,EAAE,KAAK,SAAS,EAAuB,MAAM,yBAAyB,CAAC;AAEnH,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;CACjB;AAED,MAAM,WAAW,eAAe;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,eAAe,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CA8BpE;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,CAepD"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Route generator for @teardown/navigation-metro
|
|
4
|
+
* Generates TypeScript type definitions from scanned route tree
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.generateAllRouteFiles = generateAllRouteFiles;
|
|
8
|
+
exports.buildRouteParamsInterface = buildRouteParamsInterface;
|
|
9
|
+
exports.generateRoutesFileContent = generateRoutesFileContent;
|
|
10
|
+
exports.generateLinkingFileContent = generateLinkingFileContent;
|
|
11
|
+
exports.generateRegisterFileContent = generateRegisterFileContent;
|
|
12
|
+
const node_fs_1 = require("node:fs");
|
|
13
|
+
const node_path_1 = require("node:path");
|
|
14
|
+
const file_scanner_1 = require("../scanner/file-scanner");
|
|
15
|
+
/**
|
|
16
|
+
* Generates all route files (routes.generated.ts, linking.generated.ts, register.d.ts, manifest.json)
|
|
17
|
+
*/
|
|
18
|
+
function generateAllRouteFiles(options) {
|
|
19
|
+
const { routesDir, generatedDir, prefixes, verbose } = options;
|
|
20
|
+
// Ensure generated directory exists
|
|
21
|
+
(0, node_fs_1.mkdirSync)(generatedDir, { recursive: true });
|
|
22
|
+
// Scan routes
|
|
23
|
+
const { routes, errors } = (0, file_scanner_1.scanRoutesDirectory)(routesDir);
|
|
24
|
+
if (errors.length > 0 && verbose) {
|
|
25
|
+
console.warn("[teardown/navigation] Scan warnings:", errors);
|
|
26
|
+
}
|
|
27
|
+
// Generate files
|
|
28
|
+
const routesContent = generateRoutesFileContent(routes);
|
|
29
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "routes.generated.ts"), routesContent);
|
|
30
|
+
const linkingContent = generateLinkingFileContent(routes, prefixes);
|
|
31
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "linking.generated.ts"), linkingContent);
|
|
32
|
+
const registerContent = generateRegisterFileContent();
|
|
33
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "register.d.ts"), registerContent);
|
|
34
|
+
const manifestContent = generateManifestContent(routes);
|
|
35
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "manifest.json"), JSON.stringify(manifestContent, null, 2));
|
|
36
|
+
if (verbose) {
|
|
37
|
+
const count = countRoutes(routes);
|
|
38
|
+
console.log(`[teardown/navigation] Generated ${count} routes`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds the RouteParams interface entries from the route tree
|
|
43
|
+
* Each route has all its params extracted from its full path, so no accumulation needed
|
|
44
|
+
*/
|
|
45
|
+
function buildRouteParamsInterface(routes) {
|
|
46
|
+
const result = [];
|
|
47
|
+
function traverse(nodes) {
|
|
48
|
+
for (const node of nodes) {
|
|
49
|
+
if (node.isLayout) {
|
|
50
|
+
// Layouts don't get their own route entry
|
|
51
|
+
traverse(node.children);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
result.push({
|
|
55
|
+
path: node.path,
|
|
56
|
+
params: node.params,
|
|
57
|
+
});
|
|
58
|
+
if (node.children.length > 0) {
|
|
59
|
+
traverse(node.children);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
traverse(routes);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Generates the routes.generated.ts file content
|
|
68
|
+
*/
|
|
69
|
+
function generateRoutesFileContent(routes) {
|
|
70
|
+
const routeParams = buildRouteParamsInterface(routes);
|
|
71
|
+
const lines = [
|
|
72
|
+
"// Auto-generated by @teardown/navigation",
|
|
73
|
+
"// Do not edit this file directly",
|
|
74
|
+
`// Generated at: ${new Date().toISOString()}`,
|
|
75
|
+
"",
|
|
76
|
+
"export interface RouteParams {",
|
|
77
|
+
];
|
|
78
|
+
// Add route param entries
|
|
79
|
+
for (const { path, params } of routeParams) {
|
|
80
|
+
const paramsType = params.length === 0
|
|
81
|
+
? "undefined"
|
|
82
|
+
: `{ ${params
|
|
83
|
+
.map((p) => {
|
|
84
|
+
const type = p.isCatchAll ? "string[]" : "string";
|
|
85
|
+
return `${p.name}${p.isOptional ? "?" : ""}: ${type}`;
|
|
86
|
+
})
|
|
87
|
+
.join("; ")} }`;
|
|
88
|
+
lines.push(`\t"${path}": ${paramsType};`);
|
|
89
|
+
}
|
|
90
|
+
lines.push("}");
|
|
91
|
+
lines.push("");
|
|
92
|
+
// Add type aliases
|
|
93
|
+
lines.push("export type RoutePath = keyof RouteParams;");
|
|
94
|
+
lines.push("");
|
|
95
|
+
lines.push("export type ParamsFor<T extends RoutePath> = RouteParams[T];");
|
|
96
|
+
lines.push("");
|
|
97
|
+
// Add RouteWithParams utility type
|
|
98
|
+
lines.push("export type RouteWithParams = {");
|
|
99
|
+
lines.push("\t[K in RoutePath]: RouteParams[K] extends undefined");
|
|
100
|
+
lines.push("\t\t? { path: K }");
|
|
101
|
+
lines.push("\t\t: { path: K; params: RouteParams[K] };");
|
|
102
|
+
lines.push("}[RoutePath];");
|
|
103
|
+
lines.push("");
|
|
104
|
+
// Add NavigatorType
|
|
105
|
+
lines.push('export type NavigatorType = "stack" | "tabs" | "drawer";');
|
|
106
|
+
lines.push("");
|
|
107
|
+
return lines.join("\n");
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Generates the linking.generated.ts file content
|
|
111
|
+
*/
|
|
112
|
+
function generateLinkingFileContent(routes, prefixes) {
|
|
113
|
+
const routeParams = buildRouteParamsInterface(routes);
|
|
114
|
+
const lines = [
|
|
115
|
+
"// Auto-generated by @teardown/navigation",
|
|
116
|
+
'import type { LinkingOptions } from "@react-navigation/native";',
|
|
117
|
+
'import type { RouteParams } from "./routes.generated";',
|
|
118
|
+
"",
|
|
119
|
+
];
|
|
120
|
+
// Build screens config
|
|
121
|
+
const screens = {};
|
|
122
|
+
for (const { path } of routeParams) {
|
|
123
|
+
// Convert /users/:userId to users/:userId (remove leading slash)
|
|
124
|
+
const linkingPath = path === "/" ? "" : path.slice(1);
|
|
125
|
+
screens[path] = linkingPath;
|
|
126
|
+
}
|
|
127
|
+
lines.push('export const generatedLinkingConfig: LinkingOptions<RouteParams>["config"] = {');
|
|
128
|
+
lines.push("\tscreens: {");
|
|
129
|
+
for (const [routePath, linkingPath] of Object.entries(screens)) {
|
|
130
|
+
lines.push(`\t\t"${routePath}": "${linkingPath}",`);
|
|
131
|
+
}
|
|
132
|
+
lines.push("\t},");
|
|
133
|
+
lines.push("};");
|
|
134
|
+
lines.push("");
|
|
135
|
+
// Add prefixes
|
|
136
|
+
lines.push(`export const defaultPrefixes: string[] = ${JSON.stringify(prefixes)};`);
|
|
137
|
+
lines.push("");
|
|
138
|
+
return lines.join("\n");
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Generates the register.d.ts file content
|
|
142
|
+
*/
|
|
143
|
+
function generateRegisterFileContent() {
|
|
144
|
+
const lines = [
|
|
145
|
+
"// Auto-generated by @teardown/navigation",
|
|
146
|
+
'import type { RouteParams, RoutePath } from "./routes.generated";',
|
|
147
|
+
"",
|
|
148
|
+
"declare module '@teardown/navigation' {",
|
|
149
|
+
"\tinterface Register {",
|
|
150
|
+
"\t\trouteParams: RouteParams;",
|
|
151
|
+
"\t\troutePath: RoutePath;",
|
|
152
|
+
"\t}",
|
|
153
|
+
"}",
|
|
154
|
+
"",
|
|
155
|
+
];
|
|
156
|
+
return lines.join("\n");
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Generates the manifest.json content
|
|
160
|
+
*/
|
|
161
|
+
function generateManifestContent(routes) {
|
|
162
|
+
const allRoutes = (0, file_scanner_1.flattenRoutes)(routes);
|
|
163
|
+
return {
|
|
164
|
+
generatedAt: new Date().toISOString(),
|
|
165
|
+
routeCount: allRoutes.filter((r) => !r.isLayout).length,
|
|
166
|
+
routes: allRoutes.map((r) => ({
|
|
167
|
+
path: r.path,
|
|
168
|
+
file: r.relativePath,
|
|
169
|
+
params: r.params,
|
|
170
|
+
layoutType: r.layoutType,
|
|
171
|
+
})),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Counts the number of non-layout routes
|
|
176
|
+
*/
|
|
177
|
+
function countRoutes(routes) {
|
|
178
|
+
return (0, file_scanner_1.flattenRoutes)(routes).filter((r) => !r.isLayout).length;
|
|
179
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @teardown/navigation-metro
|
|
3
|
+
* Metro plugin for type-safe file-based navigation
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Options for the Teardown Navigation Metro plugin
|
|
7
|
+
*/
|
|
8
|
+
export interface TeardownNavigationOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Path to routes directory relative to project root
|
|
11
|
+
* @default './src/routes'
|
|
12
|
+
*/
|
|
13
|
+
routesDir?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Path for generated type files
|
|
16
|
+
* @default './.teardown'
|
|
17
|
+
*/
|
|
18
|
+
generatedDir?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Deep link URL prefixes
|
|
21
|
+
* @default []
|
|
22
|
+
*/
|
|
23
|
+
prefixes?: string[];
|
|
24
|
+
/**
|
|
25
|
+
* Enable verbose logging
|
|
26
|
+
* @default false
|
|
27
|
+
*/
|
|
28
|
+
verbose?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Metro configuration type (simplified)
|
|
32
|
+
* Full type from metro-config can be used if available
|
|
33
|
+
*/
|
|
34
|
+
export interface MetroConfig {
|
|
35
|
+
projectRoot?: string;
|
|
36
|
+
watchFolders?: string[];
|
|
37
|
+
resolver?: {
|
|
38
|
+
resolveRequest?: (context: unknown, moduleName: string, platform: string | null) => {
|
|
39
|
+
filePath: string;
|
|
40
|
+
type: string;
|
|
41
|
+
} | null;
|
|
42
|
+
[key: string]: unknown;
|
|
43
|
+
};
|
|
44
|
+
transformer?: {
|
|
45
|
+
unstable_allowRequireContext?: boolean;
|
|
46
|
+
[key: string]: unknown;
|
|
47
|
+
};
|
|
48
|
+
[key: string]: unknown;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Wraps a Metro configuration with Teardown Navigation support
|
|
52
|
+
*
|
|
53
|
+
* This function:
|
|
54
|
+
* 1. Generates TypeScript type definitions on startup
|
|
55
|
+
* 2. Watches for route file changes in development
|
|
56
|
+
* 3. Configures Metro to include generated files
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```js
|
|
60
|
+
* // metro.config.js
|
|
61
|
+
* const { getDefaultConfig } = require('expo/metro-config');
|
|
62
|
+
* const { withTeardownNavigation } = require('@teardown/navigation-metro');
|
|
63
|
+
*
|
|
64
|
+
* const config = getDefaultConfig(__dirname);
|
|
65
|
+
*
|
|
66
|
+
* module.exports = withTeardownNavigation(config, {
|
|
67
|
+
* routesDir: './src/routes',
|
|
68
|
+
* generatedDir: './.teardown',
|
|
69
|
+
* prefixes: ['myapp://', 'https://myapp.com'],
|
|
70
|
+
* verbose: true,
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export declare function withTeardownNavigation(config: MetroConfig, options?: TeardownNavigationOptions): MetroConfig;
|
|
75
|
+
export type { GenerateOptions, RouteParamEntry } from "./generator/route-generator";
|
|
76
|
+
export { generateAllRouteFiles } from "./generator/route-generator";
|
|
77
|
+
export type { ParamDefinition, RouteNode, ScanError, ScanResult } from "./scanner/file-scanner";
|
|
78
|
+
export { buildUrlPath, extractParams, filePathToScreenName, flattenRoutes, scanRoutesDirectory, } from "./scanner/file-scanner";
|
|
79
|
+
export type { ValidationError } from "./validator/route-validator";
|
|
80
|
+
export { validateRoutes } from "./validator/route-validator";
|
|
81
|
+
export type { WatcherOptions } from "./watcher/file-watcher";
|
|
82
|
+
export { isWatcherRunning, startRouteWatcher, stopRouteWatcher } from "./watcher/file-watcher";
|
|
83
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAMH;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACzC;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IAEpB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE;QACV,cAAc,CAAC,EAAE,CAChB,OAAO,EAAE,OAAO,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GAAG,IAAI,KACnB;YAAE,QAAQ,EAAE,MAAM,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC;QAC/C,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACvB,CAAC;IACF,WAAW,CAAC,EAAE;QACb,4BAA4B,CAAC,EAAE,OAAO,CAAC;QACvC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACvB,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,yBAA8B,GAAG,WAAW,CAyDhH;AAED,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEpF,OAAO,EAAE,qBAAqB,EAAE,MAAM,6BAA6B,CAAC;AACpE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EACN,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,aAAa,EACb,mBAAmB,GACnB,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,YAAY,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @teardown/navigation-metro
|
|
4
|
+
* Metro plugin for type-safe file-based navigation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.stopRouteWatcher = exports.startRouteWatcher = exports.isWatcherRunning = exports.validateRoutes = exports.scanRoutesDirectory = exports.flattenRoutes = exports.filePathToScreenName = exports.extractParams = exports.buildUrlPath = exports.generateAllRouteFiles = void 0;
|
|
8
|
+
exports.withTeardownNavigation = withTeardownNavigation;
|
|
9
|
+
const node_path_1 = require("node:path");
|
|
10
|
+
const route_generator_1 = require("./generator/route-generator");
|
|
11
|
+
const file_watcher_1 = require("./watcher/file-watcher");
|
|
12
|
+
/**
|
|
13
|
+
* Wraps a Metro configuration with Teardown Navigation support
|
|
14
|
+
*
|
|
15
|
+
* This function:
|
|
16
|
+
* 1. Generates TypeScript type definitions on startup
|
|
17
|
+
* 2. Watches for route file changes in development
|
|
18
|
+
* 3. Configures Metro to include generated files
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```js
|
|
22
|
+
* // metro.config.js
|
|
23
|
+
* const { getDefaultConfig } = require('expo/metro-config');
|
|
24
|
+
* const { withTeardownNavigation } = require('@teardown/navigation-metro');
|
|
25
|
+
*
|
|
26
|
+
* const config = getDefaultConfig(__dirname);
|
|
27
|
+
*
|
|
28
|
+
* module.exports = withTeardownNavigation(config, {
|
|
29
|
+
* routesDir: './src/routes',
|
|
30
|
+
* generatedDir: './.teardown',
|
|
31
|
+
* prefixes: ['myapp://', 'https://myapp.com'],
|
|
32
|
+
* verbose: true,
|
|
33
|
+
* });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
function withTeardownNavigation(config, options = {}) {
|
|
37
|
+
const { routesDir = "./src/routes", generatedDir = "./.teardown", prefixes = [], verbose = false } = options;
|
|
38
|
+
const projectRoot = config.projectRoot ?? process.cwd();
|
|
39
|
+
const absoluteRoutesDir = (0, node_path_1.resolve)(projectRoot, routesDir);
|
|
40
|
+
const absoluteGeneratedDir = (0, node_path_1.resolve)(projectRoot, generatedDir);
|
|
41
|
+
// Generate types on startup
|
|
42
|
+
try {
|
|
43
|
+
(0, route_generator_1.generateAllRouteFiles)({
|
|
44
|
+
routesDir: absoluteRoutesDir,
|
|
45
|
+
generatedDir: absoluteGeneratedDir,
|
|
46
|
+
prefixes,
|
|
47
|
+
verbose,
|
|
48
|
+
});
|
|
49
|
+
if (verbose) {
|
|
50
|
+
console.log("[teardown/navigation] Initial generation complete");
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
console.error("[teardown/navigation] Initial generation failed:", error);
|
|
55
|
+
}
|
|
56
|
+
// Start file watcher in development
|
|
57
|
+
if (process.env.NODE_ENV !== "production") {
|
|
58
|
+
(0, file_watcher_1.startRouteWatcher)({
|
|
59
|
+
routesDir: absoluteRoutesDir,
|
|
60
|
+
generatedDir: absoluteGeneratedDir,
|
|
61
|
+
prefixes,
|
|
62
|
+
verbose,
|
|
63
|
+
onRegenerate: () => {
|
|
64
|
+
if (verbose) {
|
|
65
|
+
console.log("[teardown/navigation] Routes regenerated");
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
onError: (_errors) => {
|
|
69
|
+
if (verbose) {
|
|
70
|
+
console.error("[teardown/navigation] Validation errors during watch");
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Add watch folders for Metro
|
|
76
|
+
const watchFolders = [...(config.watchFolders ?? []), absoluteRoutesDir, absoluteGeneratedDir];
|
|
77
|
+
return {
|
|
78
|
+
...config,
|
|
79
|
+
watchFolders,
|
|
80
|
+
transformer: {
|
|
81
|
+
...config.transformer,
|
|
82
|
+
unstable_allowRequireContext: true,
|
|
83
|
+
},
|
|
84
|
+
resolver: {
|
|
85
|
+
...config.resolver,
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
// Re-export modules
|
|
90
|
+
var route_generator_2 = require("./generator/route-generator");
|
|
91
|
+
Object.defineProperty(exports, "generateAllRouteFiles", { enumerable: true, get: function () { return route_generator_2.generateAllRouteFiles; } });
|
|
92
|
+
var file_scanner_1 = require("./scanner/file-scanner");
|
|
93
|
+
Object.defineProperty(exports, "buildUrlPath", { enumerable: true, get: function () { return file_scanner_1.buildUrlPath; } });
|
|
94
|
+
Object.defineProperty(exports, "extractParams", { enumerable: true, get: function () { return file_scanner_1.extractParams; } });
|
|
95
|
+
Object.defineProperty(exports, "filePathToScreenName", { enumerable: true, get: function () { return file_scanner_1.filePathToScreenName; } });
|
|
96
|
+
Object.defineProperty(exports, "flattenRoutes", { enumerable: true, get: function () { return file_scanner_1.flattenRoutes; } });
|
|
97
|
+
Object.defineProperty(exports, "scanRoutesDirectory", { enumerable: true, get: function () { return file_scanner_1.scanRoutesDirectory; } });
|
|
98
|
+
var route_validator_1 = require("./validator/route-validator");
|
|
99
|
+
Object.defineProperty(exports, "validateRoutes", { enumerable: true, get: function () { return route_validator_1.validateRoutes; } });
|
|
100
|
+
var file_watcher_2 = require("./watcher/file-watcher");
|
|
101
|
+
Object.defineProperty(exports, "isWatcherRunning", { enumerable: true, get: function () { return file_watcher_2.isWatcherRunning; } });
|
|
102
|
+
Object.defineProperty(exports, "startRouteWatcher", { enumerable: true, get: function () { return file_watcher_2.startRouteWatcher; } });
|
|
103
|
+
Object.defineProperty(exports, "stopRouteWatcher", { enumerable: true, get: function () { return file_watcher_2.stopRouteWatcher; } });
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File scanner for @teardown/navigation-metro
|
|
3
|
+
* Scans routes directory and builds a route tree for type generation
|
|
4
|
+
*/
|
|
5
|
+
export interface RouteNode {
|
|
6
|
+
/** Screen name derived from file path */
|
|
7
|
+
name: string;
|
|
8
|
+
/** URL path for this route */
|
|
9
|
+
path: string;
|
|
10
|
+
/** Absolute file path */
|
|
11
|
+
filePath: string;
|
|
12
|
+
/** Relative file path from routes dir */
|
|
13
|
+
relativePath: string;
|
|
14
|
+
/** Extracted dynamic params */
|
|
15
|
+
params: ParamDefinition[];
|
|
16
|
+
/** Child routes */
|
|
17
|
+
children: RouteNode[];
|
|
18
|
+
/** Navigator type from _layout.tsx */
|
|
19
|
+
layoutType: "stack" | "tabs" | "drawer" | "none";
|
|
20
|
+
/** Is this an index route */
|
|
21
|
+
isIndex: boolean;
|
|
22
|
+
/** Is this a layout file */
|
|
23
|
+
isLayout: boolean;
|
|
24
|
+
/** Is this a catch-all route */
|
|
25
|
+
isCatchAll: boolean;
|
|
26
|
+
/** Route group name (from parentheses) */
|
|
27
|
+
groupName: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface ParamDefinition {
|
|
30
|
+
name: string;
|
|
31
|
+
isOptional: boolean;
|
|
32
|
+
isCatchAll: boolean;
|
|
33
|
+
}
|
|
34
|
+
export interface ScanResult {
|
|
35
|
+
routes: RouteNode[];
|
|
36
|
+
errors: ScanError[];
|
|
37
|
+
}
|
|
38
|
+
export interface ScanError {
|
|
39
|
+
file: string;
|
|
40
|
+
message: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Scans a routes directory and builds a route tree
|
|
44
|
+
*/
|
|
45
|
+
export declare function scanRoutesDirectory(routesDir: string): ScanResult;
|
|
46
|
+
/**
|
|
47
|
+
* Extracts dynamic parameters from a filename
|
|
48
|
+
*/
|
|
49
|
+
export declare function extractParams(fileName: string): ParamDefinition[];
|
|
50
|
+
/**
|
|
51
|
+
* Builds a URL path from a relative file path
|
|
52
|
+
*/
|
|
53
|
+
export declare function buildUrlPath(relativePath: string, _isIndex: boolean, isLayout: boolean): string;
|
|
54
|
+
/**
|
|
55
|
+
* Converts a file path to a screen name
|
|
56
|
+
*/
|
|
57
|
+
export declare function filePathToScreenName(relativePath: string): string;
|
|
58
|
+
/**
|
|
59
|
+
* Flattens a route tree into a flat array
|
|
60
|
+
*/
|
|
61
|
+
export declare function flattenRoutes(routes: RouteNode[]): RouteNode[];
|
|
62
|
+
//# sourceMappingURL=file-scanner.d.ts.map
|
|
@@ -0,0 +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;CACzB;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,CAwCjE;AAkFD;;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;AAsFD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,CAY9D"}
|