@jetstart/cli 1.1.4 → 1.4.0

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.
Files changed (68) hide show
  1. package/dist/cli.js +18 -1
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/android-emulator.d.ts +8 -0
  4. package/dist/commands/android-emulator.d.ts.map +1 -0
  5. package/dist/commands/android-emulator.js +280 -0
  6. package/dist/commands/android-emulator.js.map +1 -0
  7. package/dist/commands/create.d.ts +1 -6
  8. package/dist/commands/create.d.ts.map +1 -1
  9. package/dist/commands/create.js +119 -0
  10. package/dist/commands/create.js.map +1 -1
  11. package/dist/commands/dev.d.ts +1 -7
  12. package/dist/commands/dev.d.ts.map +1 -1
  13. package/dist/commands/dev.js +69 -10
  14. package/dist/commands/dev.js.map +1 -1
  15. package/dist/commands/index.d.ts +2 -0
  16. package/dist/commands/index.d.ts.map +1 -1
  17. package/dist/commands/index.js +5 -1
  18. package/dist/commands/index.js.map +1 -1
  19. package/dist/commands/install-audit.d.ts +9 -0
  20. package/dist/commands/install-audit.d.ts.map +1 -0
  21. package/dist/commands/install-audit.js +185 -0
  22. package/dist/commands/install-audit.js.map +1 -0
  23. package/dist/types/index.d.ts +22 -0
  24. package/dist/types/index.d.ts.map +1 -1
  25. package/dist/types/index.js +8 -0
  26. package/dist/types/index.js.map +1 -1
  27. package/dist/utils/android-sdk.d.ts +81 -0
  28. package/dist/utils/android-sdk.d.ts.map +1 -0
  29. package/dist/utils/android-sdk.js +432 -0
  30. package/dist/utils/android-sdk.js.map +1 -0
  31. package/dist/utils/downloader.d.ts +35 -0
  32. package/dist/utils/downloader.d.ts.map +1 -0
  33. package/dist/utils/downloader.js +214 -0
  34. package/dist/utils/downloader.js.map +1 -0
  35. package/dist/utils/emulator-deployer.d.ts +29 -0
  36. package/dist/utils/emulator-deployer.d.ts.map +1 -0
  37. package/dist/utils/emulator-deployer.js +224 -0
  38. package/dist/utils/emulator-deployer.js.map +1 -0
  39. package/dist/utils/emulator.d.ts +101 -0
  40. package/dist/utils/emulator.d.ts.map +1 -0
  41. package/dist/utils/emulator.js +410 -0
  42. package/dist/utils/emulator.js.map +1 -0
  43. package/dist/utils/java.d.ts +25 -0
  44. package/dist/utils/java.d.ts.map +1 -0
  45. package/dist/utils/java.js +363 -0
  46. package/dist/utils/java.js.map +1 -0
  47. package/dist/utils/system-tools.d.ts +93 -0
  48. package/dist/utils/system-tools.d.ts.map +1 -0
  49. package/dist/utils/system-tools.js +599 -0
  50. package/dist/utils/system-tools.js.map +1 -0
  51. package/dist/utils/template.d.ts.map +1 -1
  52. package/dist/utils/template.js +777 -748
  53. package/dist/utils/template.js.map +1 -1
  54. package/package.json +7 -3
  55. package/src/cli.ts +20 -2
  56. package/src/commands/android-emulator.ts +304 -0
  57. package/src/commands/create.ts +128 -5
  58. package/src/commands/dev.ts +71 -18
  59. package/src/commands/index.ts +3 -1
  60. package/src/commands/install-audit.ts +227 -0
  61. package/src/types/index.ts +30 -0
  62. package/src/utils/android-sdk.ts +478 -0
  63. package/src/utils/downloader.ts +201 -0
  64. package/src/utils/emulator-deployer.ts +210 -0
  65. package/src/utils/emulator.ts +463 -0
  66. package/src/utils/java.ts +369 -0
  67. package/src/utils/system-tools.ts +648 -0
  68. package/src/utils/template.ts +875 -867
@@ -0,0 +1,648 @@
1
+ /**
2
+ * System tool detection utilities
3
+ */
4
+
5
+ import { exec } from 'child_process';
6
+ import { promisify } from 'util';
7
+ import * as fs from 'fs-extra';
8
+ import * as path from 'path';
9
+ import * as os from 'os';
10
+ import { compareVersions, isVersionCompatible } from '@jetstart/shared';
11
+
12
+ const execAsync = promisify(exec);
13
+ const which = require('which');
14
+
15
+ export interface ToolInfo {
16
+ name: string;
17
+ installed: boolean;
18
+ version?: string;
19
+ path?: string;
20
+ status: 'ok' | 'warning' | 'error';
21
+ message?: string;
22
+ }
23
+
24
+ /**
25
+ * Version requirements for different tools
26
+ */
27
+ export const VERSION_REQUIREMENTS = {
28
+ node: { min: '18.0.0', recommended: '20.0.0' },
29
+ npm: { min: '9.0.0', recommended: '10.0.0' },
30
+ java: { min: '17.0.0', recommended: '17.0.0' },
31
+ gradle: { min: '8.0.0', recommended: '8.5.0' },
32
+ buildTools: { min: '33.0.0', recommended: '34.0.0' },
33
+ };
34
+
35
+ /**
36
+ * Check version compatibility status
37
+ */
38
+ function checkVersionCompatibility(
39
+ tool: keyof typeof VERSION_REQUIREMENTS,
40
+ version: string
41
+ ): { status: 'ok' | 'warning' | 'error'; message?: string } {
42
+ const req = VERSION_REQUIREMENTS[tool];
43
+ if (!req) return { status: 'ok' };
44
+
45
+ if (compareVersions(version, req.min) < 0) {
46
+ return {
47
+ status: 'error',
48
+ message: `Version ${version} is below minimum ${req.min}`,
49
+ };
50
+ }
51
+
52
+ if (compareVersions(version, req.recommended) < 0) {
53
+ return {
54
+ status: 'warning',
55
+ message: `Version ${version} is outdated (${req.recommended} recommended)`,
56
+ };
57
+ }
58
+
59
+ return { status: 'ok' };
60
+ }
61
+
62
+ /**
63
+ * Detect Node.js installation
64
+ */
65
+ export async function detectNode(): Promise<ToolInfo> {
66
+ try {
67
+ const { stdout } = await execAsync('node --version');
68
+ const version = stdout.trim().replace('v', '');
69
+ const toolPath = await which('node').catch(() => undefined);
70
+
71
+ const compat = checkVersionCompatibility('node', version);
72
+
73
+ return {
74
+ name: 'Node.js',
75
+ installed: true,
76
+ version,
77
+ path: toolPath,
78
+ status: compat.status,
79
+ message: compat.message,
80
+ };
81
+ } catch (error) {
82
+ return {
83
+ name: 'Node.js',
84
+ installed: false,
85
+ status: 'error',
86
+ message: 'Node.js not found. Install from https://nodejs.org',
87
+ };
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Detect npm installation
93
+ */
94
+ export async function detectNpm(): Promise<ToolInfo> {
95
+ try {
96
+ const { stdout } = await execAsync('npm --version');
97
+ const version = stdout.trim();
98
+ const toolPath = await which('npm').catch(() => undefined);
99
+
100
+ const compat = checkVersionCompatibility('npm', version);
101
+
102
+ return {
103
+ name: 'npm',
104
+ installed: true,
105
+ version,
106
+ path: toolPath,
107
+ status: compat.status,
108
+ message: compat.message,
109
+ };
110
+ } catch (error) {
111
+ return {
112
+ name: 'npm',
113
+ installed: false,
114
+ status: 'error',
115
+ message: 'npm not found. Install from https://nodejs.org',
116
+ };
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Parse Java version from command output
122
+ */
123
+ function parseJavaVersion(output: string): string | null {
124
+ // Handles various formats:
125
+ // openjdk version "17.0.9" 2023-10-17
126
+ // java version "1.8.0_291"
127
+ // openjdk 11.0.12 2021-07-20
128
+ const match = output.match(/version\s+"?(\d+)\.(\d+)\.(\d+)/i) ||
129
+ output.match(/openjdk\s+(\d+)\.(\d+)\.(\d+)/i);
130
+
131
+ if (match) {
132
+ // Handle old Java versioning (1.8 = Java 8)
133
+ if (match[1] === '1') {
134
+ return match[2]; // Return 8 from 1.8
135
+ }
136
+ return `${match[1]}.${match[2]}.${match[3]}`;
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ /**
143
+ * Detect Java/JDK installation
144
+ */
145
+ export async function detectJava(): Promise<ToolInfo> {
146
+ try {
147
+ const { stdout } = await execAsync('java -version 2>&1');
148
+ const version = parseJavaVersion(stdout);
149
+
150
+ if (!version) {
151
+ return {
152
+ name: 'Java/JDK',
153
+ installed: true,
154
+ status: 'warning',
155
+ message: 'Java found but version could not be determined',
156
+ };
157
+ }
158
+
159
+ const toolPath = process.env.JAVA_HOME || (await which('java').catch(() => undefined));
160
+ const compat = checkVersionCompatibility('java', version);
161
+
162
+ return {
163
+ name: 'Java/JDK',
164
+ installed: true,
165
+ version,
166
+ path: toolPath,
167
+ status: compat.status,
168
+ message: compat.message,
169
+ };
170
+ } catch (error) {
171
+ return {
172
+ name: 'Java/JDK',
173
+ installed: false,
174
+ status: 'error',
175
+ message: 'Java not found. Install JDK 17+ from https://adoptium.net',
176
+ };
177
+ }
178
+ }
179
+
180
+ /**
181
+ * Detect Gradle installation
182
+ */
183
+ export async function detectGradle(): Promise<ToolInfo> {
184
+ try {
185
+ const { stdout } = await execAsync('gradle --version');
186
+ const match = stdout.match(/Gradle\s+(\d+\.\d+(?:\.\d+)?)/);
187
+ const version = match ? match[1] : undefined;
188
+
189
+ if (!version) {
190
+ return {
191
+ name: 'Gradle',
192
+ installed: true,
193
+ status: 'warning',
194
+ message: 'Gradle found but version could not be determined',
195
+ };
196
+ }
197
+
198
+ const toolPath = await which('gradle').catch(() => undefined);
199
+ const compat = checkVersionCompatibility('gradle', version);
200
+
201
+ return {
202
+ name: 'Gradle',
203
+ installed: true,
204
+ version,
205
+ path: toolPath,
206
+ status: compat.status,
207
+ message: compat.message,
208
+ };
209
+ } catch (error) {
210
+ return {
211
+ name: 'Gradle',
212
+ installed: false,
213
+ status: 'warning',
214
+ message: 'Gradle not found (Gradle wrapper will be used)',
215
+ };
216
+ }
217
+ }
218
+
219
+ /**
220
+ * Get default Android SDK path based on platform
221
+ */
222
+ export function getDefaultSDKPath(): string {
223
+ const homeDir = os.homedir();
224
+ const platform = os.platform();
225
+
226
+ switch (platform) {
227
+ case 'win32':
228
+ return path.join(homeDir, 'AppData', 'Local', 'Android', 'Sdk');
229
+ case 'darwin':
230
+ return path.join(homeDir, 'Library', 'Android', 'sdk');
231
+ default: // Linux
232
+ return path.join(homeDir, 'Android', 'Sdk');
233
+ }
234
+ }
235
+
236
+ /**
237
+ * Find Android SDK location
238
+ */
239
+ export async function findAndroidSDK(): Promise<string | null> {
240
+ // Check environment variables first
241
+ const envPaths = [
242
+ process.env.ANDROID_HOME,
243
+ process.env.ANDROID_SDK_ROOT,
244
+ ].filter(Boolean);
245
+
246
+ for (const envPath of envPaths) {
247
+ if (envPath && await fs.pathExists(envPath)) {
248
+ return envPath;
249
+ }
250
+ }
251
+
252
+ // Check default path
253
+ const defaultPath = getDefaultSDKPath();
254
+ if (await fs.pathExists(defaultPath)) {
255
+ return defaultPath;
256
+ }
257
+
258
+ // Check alternative paths
259
+ const platform = os.platform();
260
+ const homeDir = os.homedir();
261
+ const altPaths: string[] = [];
262
+
263
+ if (platform === 'win32') {
264
+ altPaths.push(
265
+ 'C:\\Android\\Sdk',
266
+ 'C:\\Android',
267
+ path.join(homeDir, 'AppData', 'Local', 'Android', 'Sdk'),
268
+ 'C:\\Program Files (x86)\\Android\\android-sdk'
269
+ );
270
+ } else if (platform === 'darwin') {
271
+ altPaths.push(
272
+ path.join(homeDir, 'Library', 'Android', 'sdk'),
273
+ '/opt/android-sdk'
274
+ );
275
+ } else {
276
+ altPaths.push(
277
+ path.join(homeDir, 'Android', 'Sdk'),
278
+ '/opt/android-sdk'
279
+ );
280
+ }
281
+
282
+ for (const altPath of altPaths) {
283
+ if (await fs.pathExists(altPath)) {
284
+ return altPath;
285
+ }
286
+ }
287
+
288
+ return null;
289
+ }
290
+
291
+ /**
292
+ * Detect Android SDK installation
293
+ */
294
+ export async function detectAndroidSDK(): Promise<ToolInfo> {
295
+ const sdkPath = await findAndroidSDK();
296
+
297
+ if (!sdkPath) {
298
+ return {
299
+ name: 'Android SDK',
300
+ installed: false,
301
+ status: 'error',
302
+ message: 'Android SDK not found. Run "jetstart create --full-install" to install',
303
+ };
304
+ }
305
+
306
+ // Verify it's a valid SDK
307
+ const platformsPath = path.join(sdkPath, 'platforms');
308
+ const buildToolsPath = path.join(sdkPath, 'build-tools');
309
+
310
+ const isValid = (await fs.pathExists(platformsPath)) || (await fs.pathExists(buildToolsPath));
311
+
312
+ if (!isValid) {
313
+ return {
314
+ name: 'Android SDK',
315
+ installed: true,
316
+ path: sdkPath,
317
+ status: 'warning',
318
+ message: 'SDK found but appears incomplete',
319
+ };
320
+ }
321
+
322
+ return {
323
+ name: 'Android SDK',
324
+ installed: true,
325
+ path: sdkPath,
326
+ status: 'ok',
327
+ };
328
+ }
329
+
330
+ /**
331
+ * Detect Android cmdline-tools
332
+ */
333
+ export async function detectAndroidCmdlineTools(): Promise<ToolInfo> {
334
+ const sdkPath = await findAndroidSDK();
335
+
336
+ if (!sdkPath) {
337
+ return {
338
+ name: 'cmdline-tools',
339
+ installed: false,
340
+ status: 'error',
341
+ message: 'Android SDK not found',
342
+ };
343
+ }
344
+
345
+ const cmdlineToolsPath = path.join(sdkPath, 'cmdline-tools', 'latest');
346
+ const exists = await fs.pathExists(cmdlineToolsPath);
347
+
348
+ if (!exists) {
349
+ return {
350
+ name: 'cmdline-tools',
351
+ installed: false,
352
+ status: 'error',
353
+ message: 'Android cmdline-tools not installed',
354
+ };
355
+ }
356
+
357
+ // Try to get version
358
+ try {
359
+ const sdkmanagerPath = path.join(
360
+ cmdlineToolsPath,
361
+ 'bin',
362
+ os.platform() === 'win32' ? 'sdkmanager.bat' : 'sdkmanager'
363
+ );
364
+ const { stdout } = await execAsync(`"${sdkmanagerPath}" --version`);
365
+ const version = stdout.trim();
366
+
367
+ return {
368
+ name: 'cmdline-tools',
369
+ installed: true,
370
+ version,
371
+ path: cmdlineToolsPath,
372
+ status: 'ok',
373
+ };
374
+ } catch {
375
+ return {
376
+ name: 'cmdline-tools',
377
+ installed: true,
378
+ path: cmdlineToolsPath,
379
+ status: 'ok',
380
+ };
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Detect Android build-tools
386
+ */
387
+ export async function detectAndroidBuildTools(): Promise<ToolInfo> {
388
+ const sdkPath = await findAndroidSDK();
389
+
390
+ if (!sdkPath) {
391
+ return {
392
+ name: 'build-tools',
393
+ installed: false,
394
+ status: 'error',
395
+ message: 'Android SDK not found',
396
+ };
397
+ }
398
+
399
+ const buildToolsPath = path.join(sdkPath, 'build-tools');
400
+ const exists = await fs.pathExists(buildToolsPath);
401
+
402
+ if (!exists) {
403
+ return {
404
+ name: 'build-tools',
405
+ installed: false,
406
+ status: 'error',
407
+ message: 'Android build-tools not installed',
408
+ };
409
+ }
410
+
411
+ // Find latest version
412
+ try {
413
+ const versions = await fs.readdir(buildToolsPath);
414
+ if (versions.length === 0) {
415
+ return {
416
+ name: 'build-tools',
417
+ installed: false,
418
+ status: 'error',
419
+ message: 'No build-tools versions found',
420
+ };
421
+ }
422
+
423
+ // Sort versions and get latest
424
+ const latest = versions.sort((a, b) => compareVersions(b, a))[0];
425
+ const compat = checkVersionCompatibility('buildTools', latest);
426
+
427
+ return {
428
+ name: 'build-tools',
429
+ installed: true,
430
+ version: latest,
431
+ path: path.join(buildToolsPath, latest),
432
+ status: compat.status,
433
+ message: compat.message,
434
+ };
435
+ } catch {
436
+ return {
437
+ name: 'build-tools',
438
+ installed: true,
439
+ path: buildToolsPath,
440
+ status: 'ok',
441
+ };
442
+ }
443
+ }
444
+
445
+ /**
446
+ * Detect Android platform-tools (adb, fastboot)
447
+ */
448
+ export async function detectAndroidPlatformTools(): Promise<ToolInfo> {
449
+ const sdkPath = await findAndroidSDK();
450
+
451
+ if (!sdkPath) {
452
+ return {
453
+ name: 'platform-tools',
454
+ installed: false,
455
+ status: 'error',
456
+ message: 'Android SDK not found',
457
+ };
458
+ }
459
+
460
+ const platformToolsPath = path.join(sdkPath, 'platform-tools');
461
+ const exists = await fs.pathExists(platformToolsPath);
462
+
463
+ if (!exists) {
464
+ return {
465
+ name: 'platform-tools',
466
+ installed: false,
467
+ status: 'error',
468
+ message: 'Android platform-tools not installed',
469
+ };
470
+ }
471
+
472
+ // Try to get adb version
473
+ try {
474
+ const adbPath = path.join(
475
+ platformToolsPath,
476
+ os.platform() === 'win32' ? 'adb.exe' : 'adb'
477
+ );
478
+ const { stdout } = await execAsync(`"${adbPath}" version`);
479
+ const match = stdout.match(/version\s+(\d+\.\d+\.\d+)/);
480
+ const version = match ? match[1] : undefined;
481
+
482
+ return {
483
+ name: 'platform-tools',
484
+ installed: true,
485
+ version,
486
+ path: platformToolsPath,
487
+ status: 'ok',
488
+ };
489
+ } catch {
490
+ return {
491
+ name: 'platform-tools',
492
+ installed: true,
493
+ path: platformToolsPath,
494
+ status: 'ok',
495
+ };
496
+ }
497
+ }
498
+
499
+ /**
500
+ * Detect Android emulator
501
+ */
502
+ export async function detectAndroidEmulator(): Promise<ToolInfo> {
503
+ const sdkPath = await findAndroidSDK();
504
+
505
+ if (!sdkPath) {
506
+ return {
507
+ name: 'emulator',
508
+ installed: false,
509
+ status: 'error',
510
+ message: 'Android SDK not found',
511
+ };
512
+ }
513
+
514
+ const emulatorPath = path.join(sdkPath, 'emulator');
515
+ const exists = await fs.pathExists(emulatorPath);
516
+
517
+ if (!exists) {
518
+ return {
519
+ name: 'emulator',
520
+ installed: false,
521
+ status: 'warning',
522
+ message: 'Android emulator not installed',
523
+ };
524
+ }
525
+
526
+ // Try to get emulator version
527
+ try {
528
+ const emulatorBin = path.join(
529
+ emulatorPath,
530
+ os.platform() === 'win32' ? 'emulator.exe' : 'emulator'
531
+ );
532
+ const { stdout } = await execAsync(`"${emulatorBin}" -version`);
533
+ const match = stdout.match(/version\s+(\d+\.\d+\.\d+)/);
534
+ const version = match ? match[1] : undefined;
535
+
536
+ return {
537
+ name: 'emulator',
538
+ installed: true,
539
+ version,
540
+ path: emulatorPath,
541
+ status: 'ok',
542
+ };
543
+ } catch {
544
+ return {
545
+ name: 'emulator',
546
+ installed: true,
547
+ path: emulatorPath,
548
+ status: 'ok',
549
+ };
550
+ }
551
+ }
552
+
553
+ /**
554
+ * Detect specific Android platform API level
555
+ */
556
+ export async function detectAndroidPlatform(apiLevel: number): Promise<ToolInfo> {
557
+ const sdkPath = await findAndroidSDK();
558
+
559
+ if (!sdkPath) {
560
+ return {
561
+ name: `API ${apiLevel}`,
562
+ installed: false,
563
+ status: 'error',
564
+ message: 'Android SDK not found',
565
+ };
566
+ }
567
+
568
+ const platformPath = path.join(sdkPath, 'platforms', `android-${apiLevel}`);
569
+ const exists = await fs.pathExists(platformPath);
570
+
571
+ return {
572
+ name: `API ${apiLevel}`,
573
+ installed: exists,
574
+ path: exists ? platformPath : undefined,
575
+ status: exists ? 'ok' : 'error',
576
+ message: exists ? undefined : `Install with: sdkmanager "platforms;android-${apiLevel}"`,
577
+ };
578
+ }
579
+
580
+ /**
581
+ * Check JAVA_HOME environment variable
582
+ */
583
+ export async function checkJavaHome(): Promise<ToolInfo> {
584
+ const javaHome = process.env.JAVA_HOME;
585
+
586
+ if (!javaHome) {
587
+ return {
588
+ name: 'JAVA_HOME',
589
+ installed: false,
590
+ status: 'warning',
591
+ message: 'JAVA_HOME environment variable not set',
592
+ };
593
+ }
594
+
595
+ const exists = await fs.pathExists(javaHome);
596
+
597
+ if (!exists) {
598
+ return {
599
+ name: 'JAVA_HOME',
600
+ installed: false,
601
+ path: javaHome,
602
+ status: 'error',
603
+ message: `JAVA_HOME points to non-existent path: ${javaHome}`,
604
+ };
605
+ }
606
+
607
+ return {
608
+ name: 'JAVA_HOME',
609
+ installed: true,
610
+ path: javaHome,
611
+ status: 'ok',
612
+ };
613
+ }
614
+
615
+ /**
616
+ * Check ANDROID_HOME environment variable
617
+ */
618
+ export async function checkAndroidHome(): Promise<ToolInfo> {
619
+ const androidHome = process.env.ANDROID_HOME || process.env.ANDROID_SDK_ROOT;
620
+
621
+ if (!androidHome) {
622
+ return {
623
+ name: 'ANDROID_HOME',
624
+ installed: false,
625
+ status: 'warning',
626
+ message: 'ANDROID_HOME environment variable not set',
627
+ };
628
+ }
629
+
630
+ const exists = await fs.pathExists(androidHome);
631
+
632
+ if (!exists) {
633
+ return {
634
+ name: 'ANDROID_HOME',
635
+ installed: false,
636
+ path: androidHome,
637
+ status: 'error',
638
+ message: `ANDROID_HOME points to non-existent path: ${androidHome}`,
639
+ };
640
+ }
641
+
642
+ return {
643
+ name: 'ANDROID_HOME',
644
+ installed: true,
645
+ path: androidHome,
646
+ status: 'ok',
647
+ };
648
+ }