@teardown/navigation-metro 2.0.78 → 2.0.80
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 +72 -0
- package/dist/generator/generator.d.ts.map +1 -0
- package/dist/generator/generator.js +253 -0
- package/dist/generator/index.d.ts +2 -1
- package/dist/generator/index.d.ts.map +1 -1
- package/dist/generator/index.js +3 -2
- package/dist/generator/route-generator.d.ts +0 -10
- package/dist/generator/route-generator.d.ts.map +1 -1
- package/dist/generator/route-generator.js +9 -36
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +43 -75
- package/dist/scanner/file-scanner.d.ts.map +1 -1
- package/dist/scanner/file-scanner.js +4 -2
- package/dist/watcher/file-watcher.d.ts +9 -2
- package/dist/watcher/file-watcher.d.ts.map +1 -1
- package/dist/watcher/file-watcher.js +126 -67
- package/package.json +2 -2
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generator class for @teardown/navigation-metro
|
|
3
|
+
* Manages route generation with event queue, caching, and auto-templating
|
|
4
|
+
* Inspired by TanStack Router's generator architecture
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Event types for the generator
|
|
8
|
+
*/
|
|
9
|
+
export type GeneratorEventType = "create" | "update" | "delete" | "rerun";
|
|
10
|
+
/**
|
|
11
|
+
* Event representing a file system change
|
|
12
|
+
*/
|
|
13
|
+
export interface GeneratorEvent {
|
|
14
|
+
type: GeneratorEventType;
|
|
15
|
+
path?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Configuration for the Generator
|
|
19
|
+
*/
|
|
20
|
+
export interface GeneratorConfig {
|
|
21
|
+
routesDir: string;
|
|
22
|
+
generatedDir: string;
|
|
23
|
+
prefixes: string[];
|
|
24
|
+
verbose: boolean;
|
|
25
|
+
autoTemplate: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generator class that manages route file generation
|
|
29
|
+
* Features:
|
|
30
|
+
* - Event queue for batching rapid file changes
|
|
31
|
+
* - Single run-at-a-time processing to prevent race conditions
|
|
32
|
+
* - mtime-based caching to skip unchanged files
|
|
33
|
+
* - Auto-population of empty route files with templates
|
|
34
|
+
*/
|
|
35
|
+
export declare class Generator {
|
|
36
|
+
private config;
|
|
37
|
+
private fileCache;
|
|
38
|
+
private eventQueue;
|
|
39
|
+
private runPromise;
|
|
40
|
+
private lastGeneratedHash;
|
|
41
|
+
constructor(config: GeneratorConfig);
|
|
42
|
+
/**
|
|
43
|
+
* Queue an event and trigger processing
|
|
44
|
+
* Multiple calls will be batched if generation is already running
|
|
45
|
+
*/
|
|
46
|
+
run(event?: GeneratorEvent): Promise<void>;
|
|
47
|
+
/**
|
|
48
|
+
* Process all queued events
|
|
49
|
+
*/
|
|
50
|
+
private processQueue;
|
|
51
|
+
/**
|
|
52
|
+
* Check if any files have actually changed (mtime-based)
|
|
53
|
+
*/
|
|
54
|
+
private checkForChanges;
|
|
55
|
+
/**
|
|
56
|
+
* Main generation logic
|
|
57
|
+
*/
|
|
58
|
+
private generate;
|
|
59
|
+
/**
|
|
60
|
+
* Simple hash function for content comparison
|
|
61
|
+
*/
|
|
62
|
+
private hashContent;
|
|
63
|
+
/**
|
|
64
|
+
* Clear all caches (useful for testing or forced regeneration)
|
|
65
|
+
*/
|
|
66
|
+
clearCache(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Remove a file from the cache (called on file deletion)
|
|
69
|
+
*/
|
|
70
|
+
removeFromCache(filePath: string): void;
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/generator/generator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH;;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;IAgJtB;;OAEG;IACH,OAAO,CAAC,WAAW;IAWnB;;OAEG;IACH,UAAU,IAAI,IAAI;IAKlB;;OAEG;IACH,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;CAGvC"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Generator class for @teardown/navigation-metro
|
|
4
|
+
* Manages route generation with event queue, caching, and auto-templating
|
|
5
|
+
* Inspired by TanStack Router's generator architecture
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.Generator = void 0;
|
|
9
|
+
const node_fs_1 = require("node:fs");
|
|
10
|
+
const node_path_1 = require("node:path");
|
|
11
|
+
const file_scanner_1 = require("../scanner/file-scanner");
|
|
12
|
+
const route_templates_1 = require("../templates/route-templates");
|
|
13
|
+
const route_validator_1 = require("../validator/route-validator");
|
|
14
|
+
const route_generator_1 = require("./route-generator");
|
|
15
|
+
/**
|
|
16
|
+
* Generator class that manages route file generation
|
|
17
|
+
* Features:
|
|
18
|
+
* - Event queue for batching rapid file changes
|
|
19
|
+
* - Single run-at-a-time processing to prevent race conditions
|
|
20
|
+
* - mtime-based caching to skip unchanged files
|
|
21
|
+
* - Auto-population of empty route files with templates
|
|
22
|
+
*/
|
|
23
|
+
class Generator {
|
|
24
|
+
config;
|
|
25
|
+
fileCache = new Map();
|
|
26
|
+
eventQueue = [];
|
|
27
|
+
runPromise = null;
|
|
28
|
+
lastGeneratedHash = null;
|
|
29
|
+
constructor(config) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Queue an event and trigger processing
|
|
34
|
+
* Multiple calls will be batched if generation is already running
|
|
35
|
+
*/
|
|
36
|
+
async run(event) {
|
|
37
|
+
this.eventQueue.push(event ?? { type: "rerun" });
|
|
38
|
+
// If already running, the current run will process queued events
|
|
39
|
+
if (this.runPromise) {
|
|
40
|
+
return this.runPromise;
|
|
41
|
+
}
|
|
42
|
+
this.runPromise = this.processQueue();
|
|
43
|
+
try {
|
|
44
|
+
await this.runPromise;
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
this.runPromise = null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Process all queued events
|
|
52
|
+
*/
|
|
53
|
+
async processQueue() {
|
|
54
|
+
while (this.eventQueue.length > 0) {
|
|
55
|
+
// Drain the queue
|
|
56
|
+
const events = this.eventQueue.splice(0);
|
|
57
|
+
// Check if any layout files changed - these are critical for navigation type
|
|
58
|
+
const hasLayoutChange = events.some((e) => e.path?.includes("_layout.ts"));
|
|
59
|
+
if (hasLayoutChange) {
|
|
60
|
+
// Clear hash cache to force regeneration when layout changes
|
|
61
|
+
this.lastGeneratedHash = null;
|
|
62
|
+
}
|
|
63
|
+
// Check if we can skip generation (only updates, no changes)
|
|
64
|
+
const hasNonUpdateEvents = events.some((e) => e.type !== "update" || !e.path);
|
|
65
|
+
if (!hasNonUpdateEvents) {
|
|
66
|
+
// Only update events - check if any files actually changed
|
|
67
|
+
const hasChanges = await this.checkForChanges(events);
|
|
68
|
+
if (!hasChanges) {
|
|
69
|
+
if (this.config.verbose) {
|
|
70
|
+
console.log("[teardown/navigation] No changes detected, skipping regeneration");
|
|
71
|
+
}
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Run generation
|
|
76
|
+
await this.generate();
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if any files have actually changed (mtime-based)
|
|
81
|
+
*/
|
|
82
|
+
async checkForChanges(events) {
|
|
83
|
+
for (const event of events) {
|
|
84
|
+
if (!event.path)
|
|
85
|
+
continue;
|
|
86
|
+
try {
|
|
87
|
+
const stats = (0, node_fs_1.statSync)(event.path);
|
|
88
|
+
const cached = this.fileCache.get(event.path);
|
|
89
|
+
if (!cached || cached.mtimeMs !== stats.mtimeMs) {
|
|
90
|
+
return true; // File changed
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return true; // File doesn't exist or can't be read - treat as change
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Main generation logic
|
|
101
|
+
*/
|
|
102
|
+
async generate() {
|
|
103
|
+
const { routesDir, generatedDir, prefixes, verbose, autoTemplate } = this.config;
|
|
104
|
+
// Check if routes directory exists
|
|
105
|
+
if (!(0, node_fs_1.existsSync)(routesDir)) {
|
|
106
|
+
if (verbose) {
|
|
107
|
+
console.log(`[teardown/navigation] Routes directory does not exist: ${routesDir}`);
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Ensure generated directory exists
|
|
112
|
+
(0, node_fs_1.mkdirSync)(generatedDir, { recursive: true });
|
|
113
|
+
// Find all route files
|
|
114
|
+
const files = (0, file_scanner_1.findRouteFiles)(routesDir);
|
|
115
|
+
if (verbose) {
|
|
116
|
+
console.log(`[teardown/navigation] Scanning ${files.length} route files`);
|
|
117
|
+
}
|
|
118
|
+
// Auto-populate empty files before generation
|
|
119
|
+
if (autoTemplate) {
|
|
120
|
+
let populatedCount = 0;
|
|
121
|
+
for (const file of files) {
|
|
122
|
+
const absolutePath = (0, node_path_1.join)(routesDir, file);
|
|
123
|
+
// Check cache first
|
|
124
|
+
const cached = this.fileCache.get(absolutePath);
|
|
125
|
+
let stats = null;
|
|
126
|
+
try {
|
|
127
|
+
stats = (0, node_fs_1.statSync)(absolutePath);
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
continue; // Skip files that can't be read
|
|
131
|
+
}
|
|
132
|
+
// Check if file needs to be checked for emptiness
|
|
133
|
+
const needsEmptyCheck = !cached || cached.mtimeMs !== stats.mtimeMs || cached.isEmpty;
|
|
134
|
+
if (needsEmptyCheck && (0, route_templates_1.isFileEmpty)(absolutePath)) {
|
|
135
|
+
try {
|
|
136
|
+
const templateContent = (0, route_templates_1.generateRouteTemplate)(file);
|
|
137
|
+
(0, node_fs_1.writeFileSync)(absolutePath, templateContent);
|
|
138
|
+
populatedCount++;
|
|
139
|
+
// Update cache - file is no longer empty
|
|
140
|
+
const newStats = (0, node_fs_1.statSync)(absolutePath);
|
|
141
|
+
this.fileCache.set(absolutePath, {
|
|
142
|
+
mtimeMs: newStats.mtimeMs,
|
|
143
|
+
isEmpty: false,
|
|
144
|
+
});
|
|
145
|
+
if (verbose) {
|
|
146
|
+
console.log(`[teardown/navigation] Generated template for: ${file}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (verbose) {
|
|
151
|
+
console.error(`[teardown/navigation] Failed to generate template for ${file}:`, error);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
// Update cache
|
|
157
|
+
this.fileCache.set(absolutePath, {
|
|
158
|
+
mtimeMs: stats.mtimeMs,
|
|
159
|
+
isEmpty: false,
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (verbose && populatedCount === 0 && files.length > 0) {
|
|
164
|
+
console.log(`[teardown/navigation] All ${files.length} route files already have content`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
// Validate routes
|
|
168
|
+
const errors = (0, route_validator_1.validateRoutes)(routesDir);
|
|
169
|
+
const hasErrors = errors.some((e) => e.severity === "error");
|
|
170
|
+
if (hasErrors) {
|
|
171
|
+
if (verbose) {
|
|
172
|
+
console.error("[teardown/navigation] Validation errors:");
|
|
173
|
+
for (const e of errors.filter((err) => err.severity === "error")) {
|
|
174
|
+
console.error(` ${e.file}: ${e.message}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
return; // Don't generate if there are errors
|
|
178
|
+
}
|
|
179
|
+
// Scan routes and build tree
|
|
180
|
+
const { routes, errors: scanErrors } = (0, file_scanner_1.scanRoutesDirectory)(routesDir);
|
|
181
|
+
if (scanErrors.length > 0 && verbose) {
|
|
182
|
+
console.warn("[teardown/navigation] Scan warnings:", scanErrors);
|
|
183
|
+
}
|
|
184
|
+
// Generate files
|
|
185
|
+
const routesContent = (0, route_generator_1.generateRoutesFileContent)(routes);
|
|
186
|
+
const linkingContent = (0, route_generator_1.generateLinkingFileContent)(routes, prefixes);
|
|
187
|
+
const registerContent = (0, route_generator_1.generateRegisterFileContent)();
|
|
188
|
+
const routeTreeContent = (0, route_generator_1.generateRouteTreeFileContent)(routes, routesDir, generatedDir);
|
|
189
|
+
const manifestContent = {
|
|
190
|
+
generatedAt: new Date().toISOString(),
|
|
191
|
+
routeCount: routes.length,
|
|
192
|
+
routes: routes.map((r) => ({
|
|
193
|
+
path: r.path,
|
|
194
|
+
file: r.relativePath,
|
|
195
|
+
params: r.params,
|
|
196
|
+
layoutType: r.layoutType,
|
|
197
|
+
})),
|
|
198
|
+
};
|
|
199
|
+
// Create a hash of all content to detect if we need to write
|
|
200
|
+
const contentHash = this.hashContent([
|
|
201
|
+
routesContent,
|
|
202
|
+
linkingContent,
|
|
203
|
+
registerContent,
|
|
204
|
+
routeTreeContent,
|
|
205
|
+
JSON.stringify(manifestContent),
|
|
206
|
+
]);
|
|
207
|
+
// Skip writing if nothing changed
|
|
208
|
+
if (contentHash === this.lastGeneratedHash) {
|
|
209
|
+
if (verbose) {
|
|
210
|
+
console.log("[teardown/navigation] Generated content unchanged, skipping file writes");
|
|
211
|
+
}
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
// Write all files
|
|
215
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "routes.generated.ts"), routesContent);
|
|
216
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "linking.generated.ts"), linkingContent);
|
|
217
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "register.d.ts"), registerContent);
|
|
218
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "manifest.json"), JSON.stringify(manifestContent, null, 2));
|
|
219
|
+
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "routeTree.generated.ts"), routeTreeContent);
|
|
220
|
+
this.lastGeneratedHash = contentHash;
|
|
221
|
+
if (verbose) {
|
|
222
|
+
const count = routes.filter((r) => !r.isLayout).length;
|
|
223
|
+
console.log(`[teardown/navigation] Generated ${count} routes`);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Simple hash function for content comparison
|
|
228
|
+
*/
|
|
229
|
+
hashContent(contents) {
|
|
230
|
+
let hash = 0;
|
|
231
|
+
const combined = contents.join("");
|
|
232
|
+
for (let i = 0; i < combined.length; i++) {
|
|
233
|
+
const char = combined.charCodeAt(i);
|
|
234
|
+
hash = (hash << 5) - hash + char;
|
|
235
|
+
hash = hash & hash; // Convert to 32bit integer
|
|
236
|
+
}
|
|
237
|
+
return hash.toString(36);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Clear all caches (useful for testing or forced regeneration)
|
|
241
|
+
*/
|
|
242
|
+
clearCache() {
|
|
243
|
+
this.fileCache.clear();
|
|
244
|
+
this.lastGeneratedHash = null;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Remove a file from the cache (called on file deletion)
|
|
248
|
+
*/
|
|
249
|
+
removeFromCache(filePath) {
|
|
250
|
+
this.fileCache.delete(filePath);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
exports.Generator = Generator;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generator module for @teardown/navigation-metro
|
|
3
3
|
*/
|
|
4
|
-
export {
|
|
4
|
+
export { Generator, type GeneratorConfig, type GeneratorEvent, type GeneratorEventType, } from "./generator";
|
|
5
|
+
export { buildRouteParamsInterface, generateLinkingFileContent, generateRegisterFileContent, generateRoutesFileContent, type RouteParamEntry, } from "./route-generator";
|
|
5
6
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generator/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACN,SAAS,EACT,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,kBAAkB,GACvB,MAAM,aAAa,CAAC;AAErB,OAAO,EACN,yBAAyB,EACzB,0BAA0B,EAC1B,2BAA2B,EAC3B,yBAAyB,EACzB,KAAK,eAAe,GACpB,MAAM,mBAAmB,CAAC"}
|
package/dist/generator/index.js
CHANGED
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* Generator module for @teardown/navigation-metro
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.generateRoutesFileContent = exports.generateRegisterFileContent = exports.generateLinkingFileContent = exports.
|
|
6
|
+
exports.generateRoutesFileContent = exports.generateRegisterFileContent = exports.generateLinkingFileContent = exports.buildRouteParamsInterface = exports.Generator = void 0;
|
|
7
|
+
var generator_1 = require("./generator");
|
|
8
|
+
Object.defineProperty(exports, "Generator", { enumerable: true, get: function () { return generator_1.Generator; } });
|
|
7
9
|
var route_generator_1 = require("./route-generator");
|
|
8
10
|
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
11
|
Object.defineProperty(exports, "generateLinkingFileContent", { enumerable: true, get: function () { return route_generator_1.generateLinkingFileContent; } });
|
|
11
12
|
Object.defineProperty(exports, "generateRegisterFileContent", { enumerable: true, get: function () { return route_generator_1.generateRegisterFileContent; } });
|
|
12
13
|
Object.defineProperty(exports, "generateRoutesFileContent", { enumerable: true, get: function () { return route_generator_1.generateRoutesFileContent; } });
|
|
@@ -3,20 +3,10 @@
|
|
|
3
3
|
* Generates TypeScript type definitions from scanned route tree
|
|
4
4
|
*/
|
|
5
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
6
|
export interface RouteParamEntry {
|
|
13
7
|
path: string;
|
|
14
8
|
params: ParamDefinition[];
|
|
15
9
|
}
|
|
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
10
|
/**
|
|
21
11
|
* Builds the RouteParams interface entries from the route tree
|
|
22
12
|
* Each route has all its params extracted from its full path, so no accumulation needed
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route-generator.d.ts","sourceRoot":"","sources":["../../src/generator/route-generator.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
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,CAepD;AAqED,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"}
|
|
@@ -4,44 +4,14 @@
|
|
|
4
4
|
* Generates TypeScript type definitions from scanned route tree
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.generateAllRouteFiles = generateAllRouteFiles;
|
|
8
7
|
exports.buildRouteParamsInterface = buildRouteParamsInterface;
|
|
9
8
|
exports.generateRoutesFileContent = generateRoutesFileContent;
|
|
10
9
|
exports.generateLinkingFileContent = generateLinkingFileContent;
|
|
11
10
|
exports.generateRegisterFileContent = generateRegisterFileContent;
|
|
12
11
|
exports.buildRouteTreeEntries = buildRouteTreeEntries;
|
|
13
12
|
exports.generateRouteTreeFileContent = generateRouteTreeFileContent;
|
|
14
|
-
const node_fs_1 = require("node:fs");
|
|
15
13
|
const node_path_1 = require("node:path");
|
|
16
14
|
const file_scanner_1 = require("../scanner/file-scanner");
|
|
17
|
-
/**
|
|
18
|
-
* Generates all route files (routes.generated.ts, linking.generated.ts, register.d.ts, manifest.json)
|
|
19
|
-
*/
|
|
20
|
-
function generateAllRouteFiles(options) {
|
|
21
|
-
const { routesDir, generatedDir, prefixes, verbose } = options;
|
|
22
|
-
// Ensure generated directory exists
|
|
23
|
-
(0, node_fs_1.mkdirSync)(generatedDir, { recursive: true });
|
|
24
|
-
// Scan routes
|
|
25
|
-
const { routes, errors } = (0, file_scanner_1.scanRoutesDirectory)(routesDir);
|
|
26
|
-
if (errors.length > 0 && verbose) {
|
|
27
|
-
console.warn("[teardown/navigation] Scan warnings:", errors);
|
|
28
|
-
}
|
|
29
|
-
// Generate files
|
|
30
|
-
const routesContent = generateRoutesFileContent(routes);
|
|
31
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "routes.generated.ts"), routesContent);
|
|
32
|
-
const linkingContent = generateLinkingFileContent(routes, prefixes);
|
|
33
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "linking.generated.ts"), linkingContent);
|
|
34
|
-
const registerContent = generateRegisterFileContent();
|
|
35
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "register.d.ts"), registerContent);
|
|
36
|
-
const manifestContent = generateManifestContent(routes);
|
|
37
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "manifest.json"), JSON.stringify(manifestContent, null, 2));
|
|
38
|
-
const routeTreeContent = generateRouteTreeFileContent(routes, routesDir, generatedDir);
|
|
39
|
-
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(generatedDir, "routeTree.generated.ts"), routeTreeContent);
|
|
40
|
-
if (verbose) {
|
|
41
|
-
const count = countRoutes(routes);
|
|
42
|
-
console.log(`[teardown/navigation] Generated ${count} routes`);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
15
|
/**
|
|
46
16
|
* Builds the RouteParams interface entries from the route tree
|
|
47
17
|
* Each route has all its params extracted from its full path, so no accumulation needed
|
|
@@ -175,12 +145,6 @@ function generateManifestContent(routes) {
|
|
|
175
145
|
})),
|
|
176
146
|
};
|
|
177
147
|
}
|
|
178
|
-
/**
|
|
179
|
-
* Counts the number of non-layout routes
|
|
180
|
-
*/
|
|
181
|
-
function countRoutes(routes) {
|
|
182
|
-
return (0, file_scanner_1.flattenRoutes)(routes).filter((r) => !r.isLayout).length;
|
|
183
|
-
}
|
|
184
148
|
/**
|
|
185
149
|
* Converts a relative path to a valid import variable name
|
|
186
150
|
*/
|
|
@@ -405,6 +369,10 @@ function generateNavigatorCode(nav, indent) {
|
|
|
405
369
|
*/
|
|
406
370
|
function generateRouteTreeFileContent(routes, routesDir, generatedDir) {
|
|
407
371
|
const { rootNavigator, allImports } = buildNavigatorHierarchy(routes, routesDir, generatedDir);
|
|
372
|
+
// Generate a unique hash for hot reload invalidation
|
|
373
|
+
const timestamp = Date.now();
|
|
374
|
+
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
375
|
+
const hotReloadKey = `${timestamp}_${randomSuffix}`;
|
|
408
376
|
const lines = [
|
|
409
377
|
"/* eslint-disable */",
|
|
410
378
|
"// @ts-nocheck",
|
|
@@ -412,6 +380,7 @@ function generateRouteTreeFileContent(routes, routesDir, generatedDir) {
|
|
|
412
380
|
"// Auto-generated by @teardown/navigation",
|
|
413
381
|
"// Do not edit this file directly",
|
|
414
382
|
`// Generated at: ${new Date().toISOString()}`,
|
|
383
|
+
`// Hot reload key: ${hotReloadKey}`,
|
|
415
384
|
"",
|
|
416
385
|
'import type { NavigatorNode } from "@teardown/navigation";',
|
|
417
386
|
"",
|
|
@@ -467,5 +436,9 @@ function generateRouteTreeFileContent(routes, routesDir, generatedDir) {
|
|
|
467
436
|
lines.push("");
|
|
468
437
|
lines.push("export type RoutePath = (typeof routePaths)[number];");
|
|
469
438
|
lines.push("");
|
|
439
|
+
// Export hot reload key to force Metro module invalidation
|
|
440
|
+
lines.push("// Hot reload trigger - forces Metro to re-evaluate this module");
|
|
441
|
+
lines.push(`export const __HOT_RELOAD_KEY__ = "${hotReloadKey}";`);
|
|
442
|
+
lines.push("");
|
|
470
443
|
return lines.join("\n");
|
|
471
444
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -88,13 +88,14 @@ export interface MetroConfig {
|
|
|
88
88
|
* ```
|
|
89
89
|
*/
|
|
90
90
|
export declare function withTeardownNavigation(config: MetroConfig, options?: TeardownNavigationOptions): MetroConfig;
|
|
91
|
-
export type {
|
|
92
|
-
export {
|
|
91
|
+
export type { GeneratorConfig, GeneratorEvent, GeneratorEventType } from "./generator/generator";
|
|
92
|
+
export { Generator } from "./generator/generator";
|
|
93
|
+
export type { RouteParamEntry } from "./generator/route-generator";
|
|
93
94
|
export type { ParamDefinition, RouteNode, ScanError, ScanResult } from "./scanner/file-scanner";
|
|
94
|
-
export { buildUrlPath, extractParams, filePathToScreenName, flattenRoutes, scanRoutesDirectory, } from "./scanner/file-scanner";
|
|
95
|
+
export { buildUrlPath, extractParams, filePathToScreenName, findRouteFiles, flattenRoutes, scanRoutesDirectory, } from "./scanner/file-scanner";
|
|
95
96
|
export { extractParamsFromPath, fileNameToScreenName, generateLayoutTemplate, generateRouteTemplate, generateScreenTemplate, getTemplateType, isFileEmpty, screenNameToTitle, } from "./templates/route-templates";
|
|
96
97
|
export type { ValidationError } from "./validator/route-validator";
|
|
97
98
|
export { validateRoutes } from "./validator/route-validator";
|
|
98
99
|
export type { WatcherOptions } from "./watcher/file-watcher";
|
|
99
|
-
export { isWatcherRunning, startRouteWatcher, stopRouteWatcher } from "./watcher/file-watcher";
|
|
100
|
+
export { getGenerator, isWatcherRunning, startRouteWatcher, stopRouteWatcher } from "./watcher/file-watcher";
|
|
100
101
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgFH;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;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;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;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;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,GAAE,yBAA8B,GAAG,WAAW,CAkFhH;AAED,YAAY,EAAE,eAAe,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAEjG,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAElD,YAAY,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEnE,YAAY,EAAE,eAAe,EAAE,SAAS,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,EACN,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,cAAc,EACd,aAAa,EACb,mBAAmB,GACnB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACN,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,sBAAsB,EACtB,eAAe,EACf,WAAW,EACX,iBAAiB,GACjB,MAAM,6BAA6B,CAAC;AACrC,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,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,11 @@
|
|
|
4
4
|
* Metro plugin for type-safe file-based navigation
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.stopRouteWatcher = exports.startRouteWatcher = exports.isWatcherRunning = exports.validateRoutes = exports.screenNameToTitle = exports.isFileEmpty = exports.getTemplateType = exports.generateScreenTemplate = exports.generateRouteTemplate = exports.generateLayoutTemplate = exports.fileNameToScreenName = exports.extractParamsFromPath = exports.scanRoutesDirectory = exports.flattenRoutes = exports.filePathToScreenName = exports.extractParams = exports.buildUrlPath = exports.
|
|
7
|
+
exports.stopRouteWatcher = exports.startRouteWatcher = exports.isWatcherRunning = exports.getGenerator = exports.validateRoutes = exports.screenNameToTitle = exports.isFileEmpty = exports.getTemplateType = exports.generateScreenTemplate = exports.generateRouteTemplate = exports.generateLayoutTemplate = exports.fileNameToScreenName = exports.extractParamsFromPath = exports.scanRoutesDirectory = exports.flattenRoutes = exports.findRouteFiles = exports.filePathToScreenName = exports.extractParams = exports.buildUrlPath = exports.Generator = void 0;
|
|
8
8
|
exports.withTeardownNavigation = withTeardownNavigation;
|
|
9
9
|
const node_fs_1 = require("node:fs");
|
|
10
10
|
const node_path_1 = require("node:path");
|
|
11
|
-
const
|
|
12
|
-
const file_scanner_1 = require("./scanner/file-scanner");
|
|
13
|
-
const route_templates_1 = require("./templates/route-templates");
|
|
11
|
+
const generator_1 = require("./generator/generator");
|
|
14
12
|
const file_watcher_1 = require("./watcher/file-watcher");
|
|
15
13
|
/**
|
|
16
14
|
* Config file names to search for (in order of priority)
|
|
@@ -65,37 +63,6 @@ function readSlugFromConfig(projectRoot) {
|
|
|
65
63
|
const config = readTeardownConfig(projectRoot);
|
|
66
64
|
return config?.slug ?? null;
|
|
67
65
|
}
|
|
68
|
-
/**
|
|
69
|
-
* Scans routes directory for empty files and populates them with templates
|
|
70
|
-
*/
|
|
71
|
-
function populateEmptyRouteFiles(routesDir, verbose) {
|
|
72
|
-
const files = (0, file_scanner_1.findRouteFiles)(routesDir);
|
|
73
|
-
if (verbose) {
|
|
74
|
-
console.log(`[teardown/navigation] Scanning ${files.length} route files for empty templates`);
|
|
75
|
-
}
|
|
76
|
-
let populatedCount = 0;
|
|
77
|
-
for (const file of files) {
|
|
78
|
-
const absolutePath = (0, node_path_1.join)(routesDir, file);
|
|
79
|
-
if ((0, route_templates_1.isFileEmpty)(absolutePath)) {
|
|
80
|
-
try {
|
|
81
|
-
const templateContent = (0, route_templates_1.generateRouteTemplate)(file);
|
|
82
|
-
(0, node_fs_1.writeFileSync)(absolutePath, templateContent);
|
|
83
|
-
populatedCount++;
|
|
84
|
-
if (verbose) {
|
|
85
|
-
console.log(`[teardown/navigation] Generated template for: ${file}`);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
catch (error) {
|
|
89
|
-
if (verbose) {
|
|
90
|
-
console.error(`[teardown/navigation] Failed to generate template for ${file}:`, error);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (verbose && populatedCount === 0 && files.length > 0) {
|
|
96
|
-
console.log(`[teardown/navigation] All ${files.length} route files already have content`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
66
|
/**
|
|
100
67
|
* Wraps a Metro configuration with Teardown Navigation support
|
|
101
68
|
*
|
|
@@ -147,26 +114,9 @@ function withTeardownNavigation(config, options = {}) {
|
|
|
147
114
|
}
|
|
148
115
|
}
|
|
149
116
|
}
|
|
150
|
-
//
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
// Generate types on startup
|
|
155
|
-
try {
|
|
156
|
-
(0, route_generator_1.generateAllRouteFiles)({
|
|
157
|
-
routesDir: absoluteRoutesDir,
|
|
158
|
-
generatedDir: absoluteGeneratedDir,
|
|
159
|
-
prefixes,
|
|
160
|
-
verbose,
|
|
161
|
-
});
|
|
162
|
-
if (verbose) {
|
|
163
|
-
console.log("[teardown/navigation] Initial generation complete");
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
catch (error) {
|
|
167
|
-
console.error("[teardown/navigation] Initial generation failed:", error);
|
|
168
|
-
}
|
|
169
|
-
// Start file watcher in development
|
|
117
|
+
// In development, start the file watcher which handles both initial generation
|
|
118
|
+
// and watching for changes. The watcher uses ignoreInitial: false to scan
|
|
119
|
+
// all existing files and auto-populate empty ones before generation.
|
|
170
120
|
if (process.env.NODE_ENV !== "production") {
|
|
171
121
|
(0, file_watcher_1.startRouteWatcher)({
|
|
172
122
|
routesDir: absoluteRoutesDir,
|
|
@@ -180,13 +130,29 @@ function withTeardownNavigation(config, options = {}) {
|
|
|
180
130
|
console.log("[teardown/navigation] Routes regenerated");
|
|
181
131
|
}
|
|
182
132
|
},
|
|
183
|
-
onError: (
|
|
133
|
+
onError: (errors) => {
|
|
184
134
|
if (verbose) {
|
|
185
|
-
console.error("[teardown/navigation] Validation errors during watch");
|
|
135
|
+
console.error("[teardown/navigation] Validation errors during watch:");
|
|
136
|
+
for (const e of errors) {
|
|
137
|
+
console.error(` ${e.file}: ${e.message}`);
|
|
138
|
+
}
|
|
186
139
|
}
|
|
187
140
|
},
|
|
188
141
|
});
|
|
189
142
|
}
|
|
143
|
+
else {
|
|
144
|
+
// In production, just run generation once (no watching)
|
|
145
|
+
const generator = new generator_1.Generator({
|
|
146
|
+
routesDir: absoluteRoutesDir,
|
|
147
|
+
generatedDir: absoluteGeneratedDir,
|
|
148
|
+
prefixes,
|
|
149
|
+
verbose,
|
|
150
|
+
autoTemplate,
|
|
151
|
+
});
|
|
152
|
+
generator.run({ type: "rerun" }).catch((error) => {
|
|
153
|
+
console.error("[teardown/navigation] Generation failed:", error);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
190
156
|
// Add watch folders for Metro
|
|
191
157
|
const watchFolders = [...(config.watchFolders ?? []), absoluteRoutesDir, absoluteGeneratedDir];
|
|
192
158
|
return {
|
|
@@ -201,27 +167,29 @@ function withTeardownNavigation(config, options = {}) {
|
|
|
201
167
|
},
|
|
202
168
|
};
|
|
203
169
|
}
|
|
204
|
-
//
|
|
205
|
-
var
|
|
206
|
-
Object.defineProperty(exports, "
|
|
207
|
-
var
|
|
208
|
-
Object.defineProperty(exports, "buildUrlPath", { enumerable: true, get: function () { return
|
|
209
|
-
Object.defineProperty(exports, "extractParams", { enumerable: true, get: function () { return
|
|
210
|
-
Object.defineProperty(exports, "filePathToScreenName", { enumerable: true, get: function () { return
|
|
211
|
-
Object.defineProperty(exports, "
|
|
212
|
-
Object.defineProperty(exports, "
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
Object.defineProperty(exports, "
|
|
216
|
-
Object.defineProperty(exports, "
|
|
217
|
-
Object.defineProperty(exports, "
|
|
218
|
-
Object.defineProperty(exports, "
|
|
219
|
-
Object.defineProperty(exports, "
|
|
220
|
-
Object.defineProperty(exports, "
|
|
221
|
-
Object.defineProperty(exports, "
|
|
170
|
+
// Export Generator class
|
|
171
|
+
var generator_2 = require("./generator/generator");
|
|
172
|
+
Object.defineProperty(exports, "Generator", { enumerable: true, get: function () { return generator_2.Generator; } });
|
|
173
|
+
var file_scanner_1 = require("./scanner/file-scanner");
|
|
174
|
+
Object.defineProperty(exports, "buildUrlPath", { enumerable: true, get: function () { return file_scanner_1.buildUrlPath; } });
|
|
175
|
+
Object.defineProperty(exports, "extractParams", { enumerable: true, get: function () { return file_scanner_1.extractParams; } });
|
|
176
|
+
Object.defineProperty(exports, "filePathToScreenName", { enumerable: true, get: function () { return file_scanner_1.filePathToScreenName; } });
|
|
177
|
+
Object.defineProperty(exports, "findRouteFiles", { enumerable: true, get: function () { return file_scanner_1.findRouteFiles; } });
|
|
178
|
+
Object.defineProperty(exports, "flattenRoutes", { enumerable: true, get: function () { return file_scanner_1.flattenRoutes; } });
|
|
179
|
+
Object.defineProperty(exports, "scanRoutesDirectory", { enumerable: true, get: function () { return file_scanner_1.scanRoutesDirectory; } });
|
|
180
|
+
var route_templates_1 = require("./templates/route-templates");
|
|
181
|
+
Object.defineProperty(exports, "extractParamsFromPath", { enumerable: true, get: function () { return route_templates_1.extractParamsFromPath; } });
|
|
182
|
+
Object.defineProperty(exports, "fileNameToScreenName", { enumerable: true, get: function () { return route_templates_1.fileNameToScreenName; } });
|
|
183
|
+
Object.defineProperty(exports, "generateLayoutTemplate", { enumerable: true, get: function () { return route_templates_1.generateLayoutTemplate; } });
|
|
184
|
+
Object.defineProperty(exports, "generateRouteTemplate", { enumerable: true, get: function () { return route_templates_1.generateRouteTemplate; } });
|
|
185
|
+
Object.defineProperty(exports, "generateScreenTemplate", { enumerable: true, get: function () { return route_templates_1.generateScreenTemplate; } });
|
|
186
|
+
Object.defineProperty(exports, "getTemplateType", { enumerable: true, get: function () { return route_templates_1.getTemplateType; } });
|
|
187
|
+
Object.defineProperty(exports, "isFileEmpty", { enumerable: true, get: function () { return route_templates_1.isFileEmpty; } });
|
|
188
|
+
Object.defineProperty(exports, "screenNameToTitle", { enumerable: true, get: function () { return route_templates_1.screenNameToTitle; } });
|
|
222
189
|
var route_validator_1 = require("./validator/route-validator");
|
|
223
190
|
Object.defineProperty(exports, "validateRoutes", { enumerable: true, get: function () { return route_validator_1.validateRoutes; } });
|
|
224
191
|
var file_watcher_2 = require("./watcher/file-watcher");
|
|
192
|
+
Object.defineProperty(exports, "getGenerator", { enumerable: true, get: function () { return file_watcher_2.getGenerator; } });
|
|
225
193
|
Object.defineProperty(exports, "isWatcherRunning", { enumerable: true, get: function () { return file_watcher_2.isWatcherRunning; } });
|
|
226
194
|
Object.defineProperty(exports, "startRouteWatcher", { enumerable: true, get: function () { return file_watcher_2.startRouteWatcher; } });
|
|
227
195
|
Object.defineProperty(exports, "stopRouteWatcher", { enumerable: true, get: function () { return file_watcher_2.stopRouteWatcher; } });
|
|
@@ -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;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;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAqCjE;AAuCD;;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;
|
|
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;AAED;;GAEG;AAEH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAqCjE;AAuCD;;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"}
|
|
@@ -165,14 +165,16 @@ function filePathToScreenName(relativePath) {
|
|
|
165
165
|
}
|
|
166
166
|
/**
|
|
167
167
|
* Detects the layout type from a _layout.tsx file
|
|
168
|
+
* Uses regex to handle whitespace variations: type: "tabs", type : 'tabs', etc.
|
|
168
169
|
*/
|
|
169
170
|
function detectLayoutType(absolutePath) {
|
|
170
171
|
try {
|
|
171
172
|
const content = (0, node_fs_1.readFileSync)(absolutePath, "utf-8");
|
|
172
|
-
|
|
173
|
+
// Use regex to handle whitespace variations and both quote styles
|
|
174
|
+
if (/type\s*:\s*['"]tabs['"]/.test(content)) {
|
|
173
175
|
return "tabs";
|
|
174
176
|
}
|
|
175
|
-
if (
|
|
177
|
+
if (/type\s*:\s*['"]drawer['"]/.test(content)) {
|
|
176
178
|
return "drawer";
|
|
177
179
|
}
|
|
178
180
|
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* File watcher for @teardown/navigation-metro
|
|
3
|
-
* Watches
|
|
3
|
+
* Watches routes directory and triggers regeneration on changes
|
|
4
|
+
* Uses directory-based watching (not glob) to detect files in new subdirectories
|
|
4
5
|
*/
|
|
5
|
-
import {
|
|
6
|
+
import { Generator } from "../generator/generator";
|
|
7
|
+
import type { ValidationError } from "../validator/route-validator";
|
|
6
8
|
export interface WatcherOptions {
|
|
7
9
|
routesDir: string;
|
|
8
10
|
generatedDir: string;
|
|
@@ -24,6 +26,7 @@ export interface WatcherOptions {
|
|
|
24
26
|
}
|
|
25
27
|
/**
|
|
26
28
|
* Starts watching the routes directory for changes
|
|
29
|
+
* Uses directory-based watching to detect files in newly created subdirectories
|
|
27
30
|
* Returns a cleanup function to stop watching
|
|
28
31
|
*/
|
|
29
32
|
export declare function startRouteWatcher(options: WatcherOptions): () => void;
|
|
@@ -35,4 +38,8 @@ export declare function stopRouteWatcher(): void;
|
|
|
35
38
|
* Checks if the watcher is currently running
|
|
36
39
|
*/
|
|
37
40
|
export declare function isWatcherRunning(): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Gets the current generator instance (for testing)
|
|
43
|
+
*/
|
|
44
|
+
export declare function getGenerator(): Generator | null;
|
|
38
45
|
//# sourceMappingURL=file-watcher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/file-watcher.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/file-watcher.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,SAAS,EAAiD,MAAM,wBAAwB,CAAC;AAClG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAEpE,MAAM,WAAW,cAAc;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACrB;AAyDD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,IAAI,CA4IrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,SAAS,GAAG,IAAI,CAE/C"}
|
|
@@ -1,21 +1,67 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* File watcher for @teardown/navigation-metro
|
|
4
|
-
* Watches
|
|
4
|
+
* Watches routes directory and triggers regeneration on changes
|
|
5
|
+
* Uses directory-based watching (not glob) to detect files in new subdirectories
|
|
5
6
|
*/
|
|
6
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
8
|
exports.startRouteWatcher = startRouteWatcher;
|
|
8
9
|
exports.stopRouteWatcher = stopRouteWatcher;
|
|
9
10
|
exports.isWatcherRunning = isWatcherRunning;
|
|
10
|
-
|
|
11
|
+
exports.getGenerator = getGenerator;
|
|
11
12
|
const node_path_1 = require("node:path");
|
|
12
13
|
const chokidar_1 = require("chokidar");
|
|
13
|
-
const
|
|
14
|
-
const route_templates_1 = require("../templates/route-templates");
|
|
15
|
-
const route_validator_1 = require("../validator/route-validator");
|
|
14
|
+
const generator_1 = require("../generator/generator");
|
|
16
15
|
let watcherInstance = null;
|
|
16
|
+
let generatorInstance = null;
|
|
17
|
+
/**
|
|
18
|
+
* Checks if a path is a valid route file
|
|
19
|
+
* Filters to only .ts/.tsx files, excludes test files and node_modules
|
|
20
|
+
*/
|
|
21
|
+
function isRouteFile(filePath) {
|
|
22
|
+
// Must be .ts or .tsx file
|
|
23
|
+
const ext = (0, node_path_1.extname)(filePath);
|
|
24
|
+
if (ext !== ".ts" && ext !== ".tsx") {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
// Exclude test files
|
|
28
|
+
const fileName = (0, node_path_1.basename)(filePath);
|
|
29
|
+
if (fileName.includes(".test.") || fileName.includes(".spec.")) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
// Exclude node_modules
|
|
33
|
+
if (filePath.includes("node_modules")) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Exclude dotfiles (but not files starting with underscore like _layout)
|
|
37
|
+
if (fileName.startsWith(".")) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
// Exclude files starting with _ except _layout
|
|
41
|
+
const baseName = (0, node_path_1.basename)(filePath, ext);
|
|
42
|
+
if (baseName.startsWith("_") && baseName !== "_layout") {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Maps chokidar events to generator event types
|
|
49
|
+
*/
|
|
50
|
+
function mapChokidarEvent(event) {
|
|
51
|
+
switch (event) {
|
|
52
|
+
case "add":
|
|
53
|
+
return "create";
|
|
54
|
+
case "change":
|
|
55
|
+
return "update";
|
|
56
|
+
case "unlink":
|
|
57
|
+
return "delete";
|
|
58
|
+
default:
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
17
62
|
/**
|
|
18
63
|
* Starts watching the routes directory for changes
|
|
64
|
+
* Uses directory-based watching to detect files in newly created subdirectories
|
|
19
65
|
* Returns a cleanup function to stop watching
|
|
20
66
|
*/
|
|
21
67
|
function startRouteWatcher(options) {
|
|
@@ -24,8 +70,19 @@ function startRouteWatcher(options) {
|
|
|
24
70
|
if (watcherInstance) {
|
|
25
71
|
watcherInstance.close();
|
|
26
72
|
}
|
|
27
|
-
|
|
28
|
-
|
|
73
|
+
// Create generator instance
|
|
74
|
+
const generatorConfig = {
|
|
75
|
+
routesDir,
|
|
76
|
+
generatedDir,
|
|
77
|
+
prefixes,
|
|
78
|
+
verbose,
|
|
79
|
+
autoTemplate,
|
|
80
|
+
};
|
|
81
|
+
generatorInstance = new generator_1.Generator(generatorConfig);
|
|
82
|
+
// Watch the directory directly (not a glob pattern)
|
|
83
|
+
// This ensures we detect files in newly created subdirectories
|
|
84
|
+
const watcher = (0, chokidar_1.watch)(routesDir, {
|
|
85
|
+
ignoreInitial: false, // Process existing files on startup
|
|
29
86
|
persistent: true,
|
|
30
87
|
usePolling,
|
|
31
88
|
interval: usePolling ? 100 : undefined,
|
|
@@ -36,94 +93,89 @@ function startRouteWatcher(options) {
|
|
|
36
93
|
ignored: [
|
|
37
94
|
/(^|[/\\])\../, // Dotfiles
|
|
38
95
|
/node_modules/,
|
|
39
|
-
/\.test\./,
|
|
40
|
-
/\.spec\./,
|
|
41
96
|
],
|
|
97
|
+
// Important: don't use depth limit - we need to watch all subdirectories
|
|
42
98
|
});
|
|
43
|
-
|
|
44
|
-
|
|
99
|
+
let isReady = false;
|
|
100
|
+
let pendingEvents = [];
|
|
101
|
+
// Collect events during initial scan, process on ready
|
|
102
|
+
watcher.on("all", (event, filePath) => {
|
|
103
|
+
if (!isRouteFile(filePath)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (!isReady) {
|
|
107
|
+
// Queue events during initial scan
|
|
108
|
+
pendingEvents.push({ event, path: filePath });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// After ready, process events immediately
|
|
112
|
+
const eventType = mapChokidarEvent(event);
|
|
113
|
+
if (!eventType)
|
|
114
|
+
return;
|
|
115
|
+
const relativePath = (0, node_path_1.relative)(routesDir, filePath);
|
|
45
116
|
if (verbose) {
|
|
46
|
-
|
|
117
|
+
const eventName = event === "add" ? "added" : event === "change" ? "changed" : event === "unlink" ? "removed" : event;
|
|
118
|
+
console.log(`[teardown/navigation] File ${eventName}: ${relativePath}`);
|
|
47
119
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const regenerate = () => {
|
|
52
|
-
if (debounceTimer) {
|
|
53
|
-
clearTimeout(debounceTimer);
|
|
120
|
+
// Handle file deletion - remove from cache
|
|
121
|
+
if (eventType === "delete" && generatorInstance) {
|
|
122
|
+
generatorInstance.removeFromCache(filePath);
|
|
54
123
|
}
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const hasErrors = errors.some((e) => e.severity === "error");
|
|
61
|
-
if (hasErrors) {
|
|
62
|
-
if (verbose) {
|
|
63
|
-
console.error("[teardown/navigation] Validation errors:");
|
|
64
|
-
for (const e of errors.filter((err) => err.severity === "error")) {
|
|
65
|
-
console.error(` ${e.file}: ${e.message}`);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
onError?.(errors);
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
// Generate if validation passes
|
|
72
|
-
(0, route_generator_1.generateAllRouteFiles)({ routesDir, generatedDir, prefixes, verbose });
|
|
73
|
-
if (verbose) {
|
|
74
|
-
console.log("[teardown/navigation] Routes regenerated");
|
|
75
|
-
}
|
|
124
|
+
// Run generation
|
|
125
|
+
if (generatorInstance) {
|
|
126
|
+
generatorInstance
|
|
127
|
+
.run({ type: eventType, path: filePath })
|
|
128
|
+
.then(() => {
|
|
76
129
|
onRegenerate?.();
|
|
77
|
-
}
|
|
78
|
-
|
|
130
|
+
})
|
|
131
|
+
.catch((error) => {
|
|
79
132
|
if (verbose) {
|
|
80
133
|
console.error("[teardown/navigation] Generation failed:", error);
|
|
81
134
|
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
// Handle ready event - initial scan complete
|
|
139
|
+
watcher.on("ready", async () => {
|
|
140
|
+
isReady = true;
|
|
87
141
|
if (verbose) {
|
|
88
|
-
console.log(`[teardown/navigation]
|
|
142
|
+
console.log(`[teardown/navigation] Watcher ready, found ${pendingEvents.length} route files`);
|
|
89
143
|
}
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
if (autoTemplate && (0, route_templates_1.isFileEmpty)(filePath)) {
|
|
144
|
+
// Run initial generation
|
|
145
|
+
if (generatorInstance) {
|
|
93
146
|
try {
|
|
94
|
-
|
|
95
|
-
(0, node_fs_1.writeFileSync)(filePath, templateContent);
|
|
147
|
+
await generatorInstance.run({ type: "rerun" });
|
|
96
148
|
if (verbose) {
|
|
97
|
-
console.log(
|
|
149
|
+
console.log("[teardown/navigation] Initial generation complete");
|
|
98
150
|
}
|
|
151
|
+
onReady?.();
|
|
99
152
|
}
|
|
100
153
|
catch (error) {
|
|
101
154
|
if (verbose) {
|
|
102
|
-
console.error(
|
|
155
|
+
console.error("[teardown/navigation] Initial generation failed:", error);
|
|
103
156
|
}
|
|
157
|
+
onError?.([
|
|
158
|
+
{
|
|
159
|
+
file: routesDir,
|
|
160
|
+
message: String(error),
|
|
161
|
+
severity: "error",
|
|
162
|
+
},
|
|
163
|
+
]);
|
|
104
164
|
}
|
|
105
165
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
watcher.on("unlink", (filePath) => {
|
|
109
|
-
if (verbose) {
|
|
110
|
-
console.log(`[teardown/navigation] File removed: ${(0, node_path_1.relative)(routesDir, filePath)}`);
|
|
111
|
-
}
|
|
112
|
-
regenerate();
|
|
166
|
+
// Clear pending events - they were processed during initial generation
|
|
167
|
+
pendingEvents = [];
|
|
113
168
|
});
|
|
114
|
-
watcher.on("
|
|
169
|
+
watcher.on("error", (error) => {
|
|
115
170
|
if (verbose) {
|
|
116
|
-
console.
|
|
171
|
+
console.error("[teardown/navigation] Watcher error:", error);
|
|
117
172
|
}
|
|
118
|
-
regenerate();
|
|
119
173
|
});
|
|
120
174
|
watcherInstance = watcher;
|
|
121
175
|
return () => {
|
|
122
|
-
if (debounceTimer) {
|
|
123
|
-
clearTimeout(debounceTimer);
|
|
124
|
-
}
|
|
125
176
|
watcher.close();
|
|
126
177
|
watcherInstance = null;
|
|
178
|
+
generatorInstance = null;
|
|
127
179
|
};
|
|
128
180
|
}
|
|
129
181
|
/**
|
|
@@ -133,6 +185,7 @@ function stopRouteWatcher() {
|
|
|
133
185
|
if (watcherInstance) {
|
|
134
186
|
watcherInstance.close();
|
|
135
187
|
watcherInstance = null;
|
|
188
|
+
generatorInstance = null;
|
|
136
189
|
}
|
|
137
190
|
}
|
|
138
191
|
/**
|
|
@@ -141,3 +194,9 @@ function stopRouteWatcher() {
|
|
|
141
194
|
function isWatcherRunning() {
|
|
142
195
|
return watcherInstance !== null;
|
|
143
196
|
}
|
|
197
|
+
/**
|
|
198
|
+
* Gets the current generator instance (for testing)
|
|
199
|
+
*/
|
|
200
|
+
function getGenerator() {
|
|
201
|
+
return generatorInstance;
|
|
202
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teardown/navigation-metro",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.80",
|
|
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.80",
|
|
46
46
|
"@types/node": "24.10.1",
|
|
47
47
|
"typescript": "5.9.3"
|
|
48
48
|
}
|