@teardown/cli 2.0.50 → 2.0.52

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.50",
3
+ "version": "2.0.52",
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.50",
74
+ "@teardown/tsconfig": "2.0.52",
75
75
  "@types/bun": "1.3.5",
76
76
  "@types/ejs": "^3.1.5",
77
77
  "typescript": "5.9.3"
@@ -70,6 +70,7 @@ export function createInitCommand(): Command {
70
70
  .option("--skip-src", "Skip generating src folder", false)
71
71
  .option("--skip-git", "Skip generating .gitignore", false)
72
72
  .option("--skip-install", "Skip installing dependencies", false)
73
+ .option("-v, --verbose", "Show detailed output", false)
73
74
  .action(async (name: string, options) => {
74
75
  const spinner = ora("Initializing project...").start();
75
76
 
@@ -203,6 +204,9 @@ export function createInitCommand(): Command {
203
204
  // Run bun install (unless skipped)
204
205
  if (!options.skipInstall) {
205
206
  const installSpinner = ora("Installing dependencies...").start();
207
+ const startTime = Date.now();
208
+ let lastActivity = "";
209
+
206
210
  await new Promise<void>((resolve, reject) => {
207
211
  const child = spawn("bun", ["install"], {
208
212
  cwd: projectRoot,
@@ -210,25 +214,105 @@ export function createInitCommand(): Command {
210
214
  });
211
215
 
212
216
  let stderr = "";
217
+ let stdout = "";
218
+
219
+ // Update spinner with elapsed time and last activity
220
+ const progressInterval = setInterval(() => {
221
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
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
+ }
231
+ }, 1000);
232
+
233
+ // Parse bun output for status updates
234
+ child.stdout?.on("data", (data) => {
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}`));
254
+ }
255
+ }
256
+ });
257
+
213
258
  child.stderr?.on("data", (data) => {
214
- 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
+ }
215
270
  });
216
271
 
217
272
  child.on("close", (code) => {
273
+ clearInterval(progressInterval);
274
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
218
275
  if (code === 0) {
219
- installSpinner.succeed("Dependencies installed");
276
+ installSpinner.succeed(`Dependencies installed (${elapsed}s)`);
220
277
  resolve();
221
278
  } else {
222
- installSpinner.fail("Failed to install dependencies");
279
+ installSpinner.fail(`Failed to install dependencies after ${elapsed}s`);
223
280
  console.error(chalk.red(stderr));
281
+ if (stdout) {
282
+ console.log(chalk.gray("Last stdout:"), stdout.slice(-1000));
283
+ }
224
284
  reject(new Error(`bun install exited with code ${code}`));
225
285
  }
226
286
  });
227
287
 
228
288
  child.on("error", (err) => {
289
+ clearInterval(progressInterval);
229
290
  installSpinner.fail("Failed to run bun install");
291
+ console.error(chalk.red(`Error: ${err.message}`));
230
292
  reject(err);
231
293
  });
294
+
295
+ // Timeout after 5 minutes
296
+ const timeout = setTimeout(
297
+ () => {
298
+ clearInterval(progressInterval);
299
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
300
+ child.kill("SIGTERM");
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"));
307
+ if (stdout) {
308
+ console.log(chalk.gray("\nLast output:"), stdout.slice(-500));
309
+ }
310
+ reject(new Error("bun install timed out"));
311
+ },
312
+ 5 * 60 * 1000
313
+ );
314
+
315
+ child.on("close", () => clearTimeout(timeout));
232
316
  });
233
317
  }
234
318
 
@@ -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
 
@@ -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
  }
@@ -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",