@teardown/cli 2.0.51 → 2.0.53

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teardown/cli",
3
- "version": "2.0.51",
3
+ "version": "2.0.53",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -71,7 +71,7 @@
71
71
  },
72
72
  "devDependencies": {
73
73
  "@biomejs/biome": "2.3.11",
74
- "@teardown/tsconfig": "2.0.51",
74
+ "@teardown/tsconfig": "2.0.53",
75
75
  "@types/bun": "1.3.5",
76
76
  "@types/ejs": "^3.1.5",
77
77
  "typescript": "5.9.3"
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Start command - starts the Metro bundler
2
+ * Dev command - starts the Metro bundler
3
3
  */
4
4
 
5
5
  import { spawn } from "node:child_process";
@@ -25,10 +25,10 @@ function hasTeardownMetroConfig(projectRoot: string): boolean {
25
25
  }
26
26
 
27
27
  /**
28
- * Create the start command
28
+ * Create the dev command
29
29
  */
30
- export function createStartCommand(): Command {
31
- const start = new Command("start")
30
+ export function createDevCommand(): Command {
31
+ const dev = new Command("dev")
32
32
  .description("Start the Metro bundler")
33
33
  .option("-p, --port <port>", "Port to run Metro on", "8081")
34
34
  .option("--reset-cache", "Reset the Metro cache")
@@ -83,5 +83,5 @@ export function createStartCommand(): Command {
83
83
  });
84
84
  });
85
85
 
86
- return start;
86
+ return dev;
87
87
  }
@@ -205,7 +205,7 @@ export function createInitCommand(): Command {
205
205
  if (!options.skipInstall) {
206
206
  const installSpinner = ora("Installing dependencies...").start();
207
207
  const startTime = Date.now();
208
- let lastLogTime = startTime;
208
+ let lastActivity = "";
209
209
 
210
210
  await new Promise<void>((resolve, reject) => {
211
211
  const child = spawn("bun", ["install"], {
@@ -216,29 +216,57 @@ export function createInitCommand(): Command {
216
216
  let stderr = "";
217
217
  let stdout = "";
218
218
 
219
- // Log progress periodically
219
+ // Update spinner with elapsed time and last activity
220
220
  const progressInterval = setInterval(() => {
221
221
  const elapsed = Math.round((Date.now() - startTime) / 1000);
222
- installSpinner.text = `Installing dependencies... (${elapsed}s)`;
222
+ const activity = lastActivity ? ` - ${lastActivity}` : "";
223
+ installSpinner.text = `Installing dependencies... (${elapsed}s)${activity}`;
224
+
225
+ // Warn if taking too long
226
+ if (elapsed === 60) {
227
+ installSpinner.text = `Installing dependencies... (${elapsed}s) - still working, this may take a while`;
228
+ } else if (elapsed === 120) {
229
+ installSpinner.text = `Installing dependencies... (${elapsed}s) - network may be slow`;
230
+ }
223
231
  }, 1000);
224
232
 
225
- // Log stdout for debugging
233
+ // Parse bun output for status updates
226
234
  child.stdout?.on("data", (data) => {
227
- stdout += data.toString();
228
- const now = Date.now();
229
- // Log every 5 seconds if there's activity
230
- if (now - lastLogTime > 5000) {
231
- const lines = stdout.trim().split("\n");
232
- const lastLine = lines[lines.length - 1];
233
- if (lastLine && options.verbose) {
234
- console.log(chalk.gray(` [bun] ${lastLine}`));
235
+ const chunk = data.toString();
236
+ stdout += chunk;
237
+
238
+ // Extract useful status from bun output
239
+ const lines = chunk.trim().split("\n");
240
+ for (const line of lines) {
241
+ // Bun outputs package resolution info
242
+ if (line.includes("Resolving")) {
243
+ lastActivity = "resolving packages";
244
+ } else if (line.includes("Downloading")) {
245
+ lastActivity = "downloading";
246
+ } else if (line.includes("Installing")) {
247
+ lastActivity = "installing";
248
+ } else if (line.includes("Linking")) {
249
+ lastActivity = "linking";
250
+ }
251
+
252
+ if (options.verbose && line.trim()) {
253
+ console.log(chalk.gray(` [bun] ${line}`));
235
254
  }
236
- lastLogTime = now;
237
255
  }
238
256
  });
239
257
 
240
258
  child.stderr?.on("data", (data) => {
241
- stderr += data.toString();
259
+ const chunk = data.toString();
260
+ stderr += chunk;
261
+
262
+ // Bun sometimes outputs progress to stderr
263
+ if (chunk.includes("bun install")) {
264
+ lastActivity = "starting";
265
+ }
266
+
267
+ if (options.verbose && chunk.trim()) {
268
+ console.log(chalk.yellow(` [bun:err] ${chunk.trim()}`));
269
+ }
242
270
  });
243
271
 
244
272
  child.on("close", (code) => {
@@ -250,8 +278,8 @@ export function createInitCommand(): Command {
250
278
  } else {
251
279
  installSpinner.fail(`Failed to install dependencies after ${elapsed}s`);
252
280
  console.error(chalk.red(stderr));
253
- if (stdout && options.verbose) {
254
- console.log(chalk.gray("Stdout:"), stdout);
281
+ if (stdout) {
282
+ console.log(chalk.gray("Last stdout:"), stdout.slice(-1000));
255
283
  }
256
284
  reject(new Error(`bun install exited with code ${code}`));
257
285
  }
@@ -268,10 +296,16 @@ export function createInitCommand(): Command {
268
296
  const timeout = setTimeout(
269
297
  () => {
270
298
  clearInterval(progressInterval);
299
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
271
300
  child.kill("SIGTERM");
272
- installSpinner.fail("bun install timed out after 5 minutes");
301
+ installSpinner.fail(`bun install timed out after ${elapsed}s`);
302
+ console.log(chalk.yellow("\nThis usually means:"));
303
+ console.log(chalk.gray(" - Network connection issues"));
304
+ console.log(chalk.gray(" - Slow package registry response"));
305
+ console.log(chalk.gray(" - Large dependency tree"));
306
+ console.log(chalk.gray("\nTry running manually: bun install"));
273
307
  if (stdout) {
274
- console.log(chalk.gray("Last output:"), stdout.slice(-500));
308
+ console.log(chalk.gray("\nLast output:"), stdout.slice(-500));
275
309
  }
276
310
  reject(new Error("bun install timed out"));
277
311
  },
@@ -92,6 +92,8 @@ function hasPodsInstalled(): boolean {
92
92
  async function runPodInstall(): Promise<boolean> {
93
93
  const spinner = ora("Installing CocoaPods dependencies...").start();
94
94
  const iosDir = join(process.cwd(), "ios");
95
+ const startTime = Date.now();
96
+ let lastActivity = "";
95
97
 
96
98
  return new Promise((resolve) => {
97
99
  const proc = spawn("pod", ["install", "--repo-update"], {
@@ -105,9 +107,52 @@ async function runPodInstall(): Promise<boolean> {
105
107
  });
106
108
 
107
109
  let stderr = "";
108
-
109
- proc.stdout?.on("data", () => {
110
- // Ignore stdout, just wait for completion
110
+ let stdout = "";
111
+
112
+ // Update spinner with elapsed time and activity
113
+ const progressInterval = setInterval(() => {
114
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
115
+ const activity = lastActivity ? ` - ${lastActivity}` : "";
116
+ spinner.text = `Installing CocoaPods dependencies... (${elapsed}s)${activity}`;
117
+
118
+ // Warn if taking too long
119
+ if (elapsed === 60) {
120
+ spinner.text = `Installing CocoaPods dependencies... (${elapsed}s) - still working, this can take a while`;
121
+ } else if (elapsed === 120) {
122
+ spinner.text = `Installing CocoaPods dependencies... (${elapsed}s) - updating pod repos`;
123
+ } else if (elapsed === 180) {
124
+ spinner.text = `Installing CocoaPods dependencies... (${elapsed}s) - this is taking longer than usual`;
125
+ }
126
+ }, 1000);
127
+
128
+ proc.stdout?.on("data", (data) => {
129
+ const chunk = data.toString();
130
+ stdout += chunk;
131
+
132
+ // Parse CocoaPods output for status
133
+ for (const line of chunk.split("\n")) {
134
+ const trimmed = line.trim();
135
+ if (!trimmed) continue;
136
+
137
+ // Extract useful status from pod output
138
+ if (trimmed.startsWith("Analyzing dependencies")) {
139
+ lastActivity = "analyzing dependencies";
140
+ } else if (trimmed.startsWith("Downloading dependencies")) {
141
+ lastActivity = "downloading dependencies";
142
+ } else if (trimmed.startsWith("Installing")) {
143
+ // Extract pod name: "Installing PodName (1.0.0)"
144
+ const match = trimmed.match(/Installing (\S+)/);
145
+ if (match) {
146
+ lastActivity = `installing ${match[1]}`;
147
+ }
148
+ } else if (trimmed.startsWith("Generating Pods project")) {
149
+ lastActivity = "generating Pods project";
150
+ } else if (trimmed.startsWith("Integrating client project")) {
151
+ lastActivity = "integrating project";
152
+ } else if (trimmed.includes("Pod installation complete")) {
153
+ lastActivity = "completing";
154
+ }
155
+ }
111
156
  });
112
157
 
113
158
  proc.stderr?.on("data", (data) => {
@@ -115,12 +160,18 @@ async function runPodInstall(): Promise<boolean> {
115
160
  });
116
161
 
117
162
  proc.on("close", (code) => {
163
+ clearInterval(progressInterval);
164
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
165
+
118
166
  if (code === 0) {
119
- spinner.succeed("CocoaPods dependencies installed");
167
+ spinner.succeed(`CocoaPods dependencies installed (${elapsed}s)`);
120
168
  resolve(true);
121
169
  } else {
122
- spinner.fail("Failed to install CocoaPods dependencies");
170
+ spinner.fail(`Failed to install CocoaPods dependencies after ${elapsed}s`);
123
171
  console.error(chalk.red(stderr));
172
+ if (stdout) {
173
+ console.log(chalk.gray("Last output:"), stdout.slice(-500));
174
+ }
124
175
  console.log(chalk.yellow("\nTry running manually:"));
125
176
  console.log(chalk.gray(" cd ios && pod install --repo-update"));
126
177
  resolve(false);
@@ -128,9 +179,32 @@ async function runPodInstall(): Promise<boolean> {
128
179
  });
129
180
 
130
181
  proc.on("error", (err) => {
182
+ clearInterval(progressInterval);
131
183
  spinner.fail(`Failed to run pod install: ${err.message}`);
132
184
  resolve(false);
133
185
  });
186
+
187
+ // Timeout after 10 minutes (pod install can be slow)
188
+ const timeout = setTimeout(
189
+ () => {
190
+ clearInterval(progressInterval);
191
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
192
+ proc.kill("SIGTERM");
193
+ spinner.fail(`pod install timed out after ${elapsed}s`);
194
+ console.log(chalk.yellow("\nThis usually means:"));
195
+ console.log(chalk.gray(" - Network connection issues"));
196
+ console.log(chalk.gray(" - Slow CocoaPods CDN"));
197
+ console.log(chalk.gray(" - Large number of pods to install"));
198
+ console.log(chalk.gray("\nTry running manually: cd ios && pod install --repo-update"));
199
+ if (stdout) {
200
+ console.log(chalk.gray("\nLast output:"), stdout.slice(-500));
201
+ }
202
+ resolve(false);
203
+ },
204
+ 10 * 60 * 1000
205
+ );
206
+
207
+ proc.on("close", () => clearTimeout(timeout));
134
208
  });
135
209
  }
136
210
 
package/src/cli/index.ts CHANGED
@@ -3,11 +3,11 @@
3
3
  */
4
4
 
5
5
  import { Command } from "commander";
6
+ import { createDevCommand } from "./commands/dev";
6
7
  import { createInitCommand } from "./commands/init";
7
8
  import { createPluginsCommand } from "./commands/plugins";
8
9
  import { createPrebuildCommand } from "./commands/prebuild";
9
10
  import { createRunCommand } from "./commands/run";
10
- import { createStartCommand } from "./commands/start";
11
11
  import { createValidateCommand } from "./commands/validate";
12
12
 
13
13
  /**
@@ -30,7 +30,7 @@ export function createProgram(): Command {
30
30
  program.addCommand(createInitCommand());
31
31
  program.addCommand(createPluginsCommand());
32
32
  program.addCommand(createRunCommand());
33
- program.addCommand(createStartCommand());
33
+ program.addCommand(createDevCommand());
34
34
 
35
35
  // Default command (prebuild)
36
36
  program.argument("[command]").action((cmd) => {
@@ -50,10 +50,10 @@ export async function run(args: string[] = process.argv): Promise<void> {
50
50
  await program.parseAsync(args);
51
51
  }
52
52
 
53
+ export { createDevCommand } from "./commands/dev";
53
54
  export { createInitCommand } from "./commands/init";
54
55
  export { createPluginsCommand } from "./commands/plugins";
55
56
  // Export commands for testing
56
57
  export { createPrebuildCommand } from "./commands/prebuild";
57
58
  export { createRunCommand } from "./commands/run";
58
- export { createStartCommand } from "./commands/start";
59
59
  export { createValidateCommand } from "./commands/validate";
@@ -606,6 +606,9 @@ class IOSPostInstallStage implements PipelineStage {
606
606
  * Run pod install in the iOS directory
607
607
  */
608
608
  private async runPodInstall(iosDir: string, logger: Logger, repoUpdate = false): Promise<void> {
609
+ const startTime = Date.now();
610
+ let lastActivity = "";
611
+
609
612
  return new Promise((resolve, reject) => {
610
613
  // Use --repo-update for fresh installs to ensure specs are up to date
611
614
  const args = repoUpdate ? ["install", "--repo-update"] : ["install"];
@@ -624,21 +627,53 @@ class IOSPostInstallStage implements PipelineStage {
624
627
  let stdout = "";
625
628
  let stderr = "";
626
629
 
630
+ // Log progress periodically
631
+ const progressInterval = setInterval(() => {
632
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
633
+ const activity = lastActivity ? ` - ${lastActivity}` : "";
634
+ logger.info(`pod install running... (${elapsed}s)${activity}`);
635
+ }, 30000); // Log every 30 seconds
636
+
627
637
  child.stdout?.on("data", (data) => {
628
- stdout += data.toString();
629
- // Log progress for verbose output
630
- const lines = data.toString().split("\n");
638
+ const chunk = data.toString();
639
+ stdout += chunk;
640
+
641
+ // Parse CocoaPods output for status and log progress
642
+ const lines = chunk.split("\n");
631
643
  for (const line of lines) {
632
- if (line.trim()) {
633
- logger.debug(`[pod] ${line.trim()}`);
644
+ const trimmed = line.trim();
645
+ if (!trimmed) continue;
646
+
647
+ logger.debug(`[pod] ${trimmed}`);
648
+
649
+ // Extract useful status from pod output
650
+ if (trimmed.startsWith("Analyzing dependencies")) {
651
+ lastActivity = "analyzing dependencies";
652
+ logger.info("Analyzing dependencies...");
653
+ } else if (trimmed.startsWith("Downloading dependencies")) {
654
+ lastActivity = "downloading dependencies";
655
+ logger.info("Downloading dependencies...");
656
+ } else if (trimmed.startsWith("Installing")) {
657
+ // Extract pod name: "Installing PodName (1.0.0)"
658
+ const match = trimmed.match(/Installing (\S+)/);
659
+ if (match) {
660
+ lastActivity = `installing ${match[1]}`;
661
+ }
662
+ } else if (trimmed.startsWith("Generating Pods project")) {
663
+ lastActivity = "generating Pods project";
664
+ logger.info("Generating Pods project...");
665
+ } else if (trimmed.startsWith("Integrating client project")) {
666
+ lastActivity = "integrating project";
667
+ logger.info("Integrating client project...");
634
668
  }
635
669
  }
636
670
  });
637
671
 
638
672
  child.stderr?.on("data", (data) => {
639
- stderr += data.toString();
673
+ const chunk = data.toString();
674
+ stderr += chunk;
640
675
  // Also log stderr for visibility
641
- const lines = data.toString().split("\n");
676
+ const lines = chunk.split("\n");
642
677
  for (const line of lines) {
643
678
  if (line.trim()) {
644
679
  logger.debug(`[pod:err] ${line.trim()}`);
@@ -647,16 +682,43 @@ class IOSPostInstallStage implements PipelineStage {
647
682
  });
648
683
 
649
684
  child.on("close", (code) => {
685
+ clearInterval(progressInterval);
686
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
687
+
650
688
  if (code === 0) {
689
+ logger.info(`pod install completed in ${elapsed}s`);
651
690
  resolve();
652
691
  } else {
692
+ logger.error(`pod install failed after ${elapsed}s`);
693
+ if (stdout) {
694
+ logger.debug(`Last stdout: ${stdout.slice(-500)}`);
695
+ }
653
696
  reject(new Error(`pod install exited with code ${code}: ${stderr || stdout}`));
654
697
  }
655
698
  });
656
699
 
657
700
  child.on("error", (err) => {
701
+ clearInterval(progressInterval);
658
702
  reject(err);
659
703
  });
704
+
705
+ // Timeout after 10 minutes
706
+ const timeout = setTimeout(
707
+ () => {
708
+ clearInterval(progressInterval);
709
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
710
+ child.kill("SIGTERM");
711
+ logger.error(`pod install timed out after ${elapsed}s`);
712
+ logger.error("This usually means network issues or slow CocoaPods CDN");
713
+ if (stdout) {
714
+ logger.debug(`Last output: ${stdout.slice(-500)}`);
715
+ }
716
+ reject(new Error("pod install timed out"));
717
+ },
718
+ 10 * 60 * 1000
719
+ );
720
+
721
+ child.on("close", () => clearTimeout(timeout));
660
722
  });
661
723
  }
662
724
  }
@@ -3,7 +3,7 @@
3
3
  "version": "<%= version %>",
4
4
  "private": true,
5
5
  "scripts": {
6
- "start": "teardown start",
6
+ "dev": "teardown dev",
7
7
  "ios": "teardown run ios",
8
8
  "android": "teardown run android",
9
9
  "lint": "eslint .",
@@ -11,6 +11,7 @@
11
11
  "prebuild:clean": "teardown prebuild --clean"
12
12
  },
13
13
  "dependencies": {
14
+ "@gorhom/bottom-sheet": "^5",
14
15
  "@react-navigation/bottom-tabs": "^7.2.0",
15
16
  "@react-navigation/native": "^7.0.14",
16
17
  "@react-navigation/native-stack": "^7.2.0",
@@ -19,13 +20,17 @@
19
20
  "heroui-native": "^1.0.0-beta.12",
20
21
  "react": "18.3.1",
21
22
  "react-native": "0.78.2",
23
+ "react-native-gesture-handler": "^2.28.0",
22
24
  "react-native-mmkv": "^3.2.0",
23
- "react-native-reanimated": "^3.16.0",
24
- "react-native-safe-area-context": "^4.14.0",
25
- "react-native-screens": "~4.18.0",
26
- "react-native-svg": "^15.8.0",
27
- "uniwind": "^1.2.0",
28
- "tailwindcss": "^4.0.0"
25
+ "react-native-reanimated": "~4.1.1",
26
+ "react-native-safe-area-context": "~5.6.0",
27
+ "react-native-screens": "~4.16.0",
28
+ "react-native-svg": "15.12.1",
29
+ "react-native-worklets": "0.5.1",
30
+ "tailwind-merge": "^3.4.0",
31
+ "tailwind-variants": "^3.2.2",
32
+ "tailwindcss": "^4.0.0",
33
+ "uniwind": "^1.2.0"
29
34
  },
30
35
  "devDependencies": {
31
36
  "@babel/core": "^7.26.0",