@teardown/navigation-metro 2.0.74 → 2.0.76

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/index.d.ts CHANGED
@@ -8,7 +8,8 @@
8
8
  export interface TeardownNavigationOptions {
9
9
  /**
10
10
  * Path to routes directory relative to project root
11
- * @default './src/routes'
11
+ * If not provided, reads from teardown.config.ts navigation.routesDir
12
+ * @default './src/_routes'
12
13
  */
13
14
  routesDir?: string;
14
15
  /**
@@ -31,6 +32,12 @@ export interface TeardownNavigationOptions {
31
32
  * @default true
32
33
  */
33
34
  autoTemplate?: boolean;
35
+ /**
36
+ * Use polling mode instead of native file events
37
+ * Enable this for VMs, network drives, or systems where fsevents doesn't work reliably
38
+ * @default false
39
+ */
40
+ usePolling?: boolean;
34
41
  }
35
42
  /**
36
43
  * Metro configuration type (simplified)
@@ -63,13 +70,17 @@ export interface MetroConfig {
63
70
  * @example
64
71
  * ```js
65
72
  * // metro.config.js
66
- * const { getDefaultConfig } = require('expo/metro-config');
73
+ * const { getDefaultConfig } = require('@react-native/metro-config');
67
74
  * const { withTeardownNavigation } = require('@teardown/navigation-metro');
68
75
  *
69
76
  * const config = getDefaultConfig(__dirname);
70
77
  *
78
+ * // Options are read from teardown.config.ts navigation section by default
79
+ * module.exports = withTeardownNavigation(config);
80
+ *
81
+ * // Or override with explicit options:
71
82
  * module.exports = withTeardownNavigation(config, {
72
- * routesDir: './src/routes',
83
+ * routesDir: './src/_routes',
73
84
  * generatedDir: './.teardown',
74
85
  * prefixes: ['myapp://', 'https://myapp.com'],
75
86
  * verbose: true,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAmCH;;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;IAElB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACvB;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,CAsEhH;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,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,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA0EH;;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,CAgFhH;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,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,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC"}
package/dist/index.js CHANGED
@@ -15,20 +15,35 @@ const file_watcher_1 = require("./watcher/file-watcher");
15
15
  */
16
16
  const CONFIG_FILE_NAMES = ["teardown.config.ts", "teardown.config.js", "teardown.config.mjs", "launchpad.config.ts"];
17
17
  /**
18
- * Read the slug from the Teardown config file
19
- * This is a simple synchronous parser that extracts the slug value
18
+ * Read the navigation config from the Teardown config file
19
+ * This is a simple synchronous parser that extracts values using regex
20
20
  */
21
- function readSlugFromConfig(projectRoot) {
21
+ function readTeardownConfig(projectRoot) {
22
22
  for (const fileName of CONFIG_FILE_NAMES) {
23
23
  const configPath = (0, node_path_1.resolve)(projectRoot, fileName);
24
24
  if ((0, node_fs_1.existsSync)(configPath)) {
25
25
  try {
26
26
  const content = (0, node_fs_1.readFileSync)(configPath, "utf-8");
27
+ const config = {};
27
28
  // Extract slug using regex - handles both single and double quotes
28
29
  const slugMatch = content.match(/slug:\s*['"]([^'"]+)['"]/);
29
30
  if (slugMatch) {
30
- return slugMatch[1];
31
+ config.slug = slugMatch[1];
32
+ }
33
+ // Extract navigation config values
34
+ const routesDirMatch = content.match(/routesDir:\s*['"]([^'"]+)['"]/);
35
+ if (routesDirMatch) {
36
+ config.routesDir = routesDirMatch[1];
37
+ }
38
+ const generatedDirMatch = content.match(/generatedDir:\s*['"]([^'"]+)['"]/);
39
+ if (generatedDirMatch) {
40
+ config.generatedDir = generatedDirMatch[1];
31
41
  }
42
+ const autoTemplateMatch = content.match(/autoTemplate:\s*(true|false)/);
43
+ if (autoTemplateMatch) {
44
+ config.autoTemplate = autoTemplateMatch[1] === "true";
45
+ }
46
+ return config;
32
47
  }
33
48
  catch {
34
49
  // Ignore read errors and try next config file
@@ -37,6 +52,13 @@ function readSlugFromConfig(projectRoot) {
37
52
  }
38
53
  return null;
39
54
  }
55
+ /**
56
+ * Read the slug from the Teardown config file (legacy helper)
57
+ */
58
+ function readSlugFromConfig(projectRoot) {
59
+ const config = readTeardownConfig(projectRoot);
60
+ return config?.slug ?? null;
61
+ }
40
62
  /**
41
63
  * Wraps a Metro configuration with Teardown Navigation support
42
64
  *
@@ -48,13 +70,17 @@ function readSlugFromConfig(projectRoot) {
48
70
  * @example
49
71
  * ```js
50
72
  * // metro.config.js
51
- * const { getDefaultConfig } = require('expo/metro-config');
73
+ * const { getDefaultConfig } = require('@react-native/metro-config');
52
74
  * const { withTeardownNavigation } = require('@teardown/navigation-metro');
53
75
  *
54
76
  * const config = getDefaultConfig(__dirname);
55
77
  *
78
+ * // Options are read from teardown.config.ts navigation section by default
79
+ * module.exports = withTeardownNavigation(config);
80
+ *
81
+ * // Or override with explicit options:
56
82
  * module.exports = withTeardownNavigation(config, {
57
- * routesDir: './src/routes',
83
+ * routesDir: './src/_routes',
58
84
  * generatedDir: './.teardown',
59
85
  * prefixes: ['myapp://', 'https://myapp.com'],
60
86
  * verbose: true,
@@ -62,8 +88,15 @@ function readSlugFromConfig(projectRoot) {
62
88
  * ```
63
89
  */
64
90
  function withTeardownNavigation(config, options = {}) {
65
- const { routesDir = "./src/routes", generatedDir = "./.teardown", verbose = false, autoTemplate = true } = options;
66
91
  const projectRoot = config.projectRoot ?? process.cwd();
92
+ // Read from teardown.config.ts if options not provided
93
+ const teardownConfig = readTeardownConfig(projectRoot);
94
+ // Use options if provided, fallback to teardown.config.ts, then defaults
95
+ const routesDir = options.routesDir ?? teardownConfig?.routesDir ?? "./src/_routes";
96
+ const generatedDir = options.generatedDir ?? teardownConfig?.generatedDir ?? "./.teardown";
97
+ const autoTemplate = options.autoTemplate ?? teardownConfig?.autoTemplate ?? true;
98
+ const verbose = options.verbose ?? false;
99
+ const usePolling = options.usePolling ?? false;
67
100
  const absoluteRoutesDir = (0, node_path_1.resolve)(projectRoot, routesDir);
68
101
  const absoluteGeneratedDir = (0, node_path_1.resolve)(projectRoot, generatedDir);
69
102
  // If no prefixes provided, try to read slug from teardown config
@@ -100,6 +133,7 @@ function withTeardownNavigation(config, options = {}) {
100
133
  prefixes,
101
134
  verbose,
102
135
  autoTemplate,
136
+ usePolling,
103
137
  onRegenerate: () => {
104
138
  if (verbose) {
105
139
  console.log("[teardown/navigation] Routes regenerated");
@@ -13,8 +13,14 @@ export interface WatcherOptions {
13
13
  * @default true
14
14
  */
15
15
  autoTemplate?: boolean;
16
+ /**
17
+ * Use polling mode instead of native events (for VMs, network drives)
18
+ * @default false
19
+ */
20
+ usePolling?: boolean;
16
21
  onRegenerate?: () => void;
17
22
  onError?: (errors: ValidationError[]) => void;
23
+ onReady?: () => void;
18
24
  }
19
25
  /**
20
26
  * Starts watching the routes directory for changes
@@ -1 +1 @@
1
- {"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/file-watcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,KAAK,eAAe,EAAkB,MAAM,8BAA8B,CAAC;AAEpF,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,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,IAAI,CAAC;CAC9C;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,IAAI,CA+GrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
1
+ {"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/watcher/file-watcher.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,EAAE,KAAK,eAAe,EAAkB,MAAM,8BAA8B,CAAC;AAEpF,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;AAID;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,IAAI,CAoIrE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAKvC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C"}
@@ -19,13 +19,20 @@ let watcherInstance = null;
19
19
  * Returns a cleanup function to stop watching
20
20
  */
21
21
  function startRouteWatcher(options) {
22
- const { routesDir, generatedDir, prefixes, verbose, autoTemplate = true, onRegenerate, onError } = options;
22
+ const { routesDir, generatedDir, prefixes, verbose, autoTemplate = true, usePolling = false, onRegenerate, onError, onReady, } = options;
23
23
  // Close existing watcher if any
24
24
  if (watcherInstance) {
25
25
  watcherInstance.close();
26
26
  }
27
27
  const watcher = (0, chokidar_1.watch)((0, node_path_1.join)(routesDir, "**/*.{ts,tsx}"), {
28
28
  ignoreInitial: true,
29
+ persistent: true,
30
+ usePolling,
31
+ interval: usePolling ? 100 : undefined,
32
+ awaitWriteFinish: {
33
+ stabilityThreshold: 100,
34
+ pollInterval: 50,
35
+ },
29
36
  ignored: [
30
37
  /(^|[/\\])\../, // Dotfiles
31
38
  /node_modules/,
@@ -33,6 +40,13 @@ function startRouteWatcher(options) {
33
40
  /\.spec\./,
34
41
  ],
35
42
  });
43
+ // Log when watcher is ready
44
+ watcher.on("ready", () => {
45
+ if (verbose) {
46
+ console.log("[teardown/navigation] Watcher ready and watching for changes");
47
+ }
48
+ onReady?.();
49
+ });
36
50
  let debounceTimer = null;
37
51
  const regenerate = () => {
38
52
  if (debounceTimer) {
@@ -73,27 +87,23 @@ function startRouteWatcher(options) {
73
87
  if (verbose) {
74
88
  console.log(`[teardown/navigation] File added: ${relativePath}`);
75
89
  }
76
- // Add delay to let filesystem settle before checking if file is empty
77
- // This prevents race conditions where the file hasn't finished being created
78
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: template generation with error handling
79
- setTimeout(() => {
80
- // Auto-populate empty files with template content
81
- if (autoTemplate && (0, route_templates_1.isFileEmpty)(filePath)) {
82
- try {
83
- const templateContent = (0, route_templates_1.generateRouteTemplate)(relativePath);
84
- (0, node_fs_1.writeFileSync)(filePath, templateContent);
85
- if (verbose) {
86
- console.log(`[teardown/navigation] Generated template for: ${relativePath}`);
87
- }
90
+ // awaitWriteFinish handles waiting for file stability, so no setTimeout needed
91
+ // Auto-populate empty files with template content
92
+ if (autoTemplate && (0, route_templates_1.isFileEmpty)(filePath)) {
93
+ try {
94
+ const templateContent = (0, route_templates_1.generateRouteTemplate)(relativePath);
95
+ (0, node_fs_1.writeFileSync)(filePath, templateContent);
96
+ if (verbose) {
97
+ console.log(`[teardown/navigation] Generated template for: ${relativePath}`);
88
98
  }
89
- catch (error) {
90
- if (verbose) {
91
- console.error(`[teardown/navigation] Failed to generate template for ${relativePath}:`, error);
92
- }
99
+ }
100
+ catch (error) {
101
+ if (verbose) {
102
+ console.error(`[teardown/navigation] Failed to generate template for ${relativePath}:`, error);
93
103
  }
94
104
  }
95
- regenerate();
96
- }, 50);
105
+ }
106
+ regenerate();
97
107
  });
98
108
  watcher.on("unlink", (filePath) => {
99
109
  if (verbose) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teardown/navigation-metro",
3
- "version": "2.0.74",
3
+ "version": "2.0.76",
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.74",
45
+ "@teardown/tsconfig": "2.0.76",
46
46
  "@types/node": "24.10.1",
47
47
  "typescript": "5.9.3"
48
48
  }