@jskit-ai/jskit-cli 0.2.74 → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import { spawn } from "node:child_process";
3
- import { readFile } from "node:fs/promises";
3
+ import { readFile, writeFile } from "node:fs/promises";
4
4
  import {
5
5
  createColorFormatter,
6
6
  writeWrappedLines
@@ -23,6 +23,10 @@ import {
23
23
  } from "./mobileShellSupport.js";
24
24
  const CAPACITOR_RUNTIME_PACKAGE_ID = "@jskit-ai/mobile-capacitor";
25
25
  const MOBILE_NOTES_RELATIVE_PATH = path.join(".jskit", "mobile-capacitor.md");
26
+ const MANAGED_MOBILE_FILE_RELATIVE_PATHS = Object.freeze([
27
+ "capacitor.config.json",
28
+ MOBILE_NOTES_RELATIVE_PATH
29
+ ]);
26
30
 
27
31
  async function collectManagedMobileFileDriftIssues({
28
32
  ctx,
@@ -34,12 +38,7 @@ async function collectManagedMobileFileDriftIssues({
34
38
  path: pathModule,
35
39
  normalizeRelativePath
36
40
  } = ctx;
37
- const managedRelativePaths = [
38
- "capacitor.config.json",
39
- MOBILE_NOTES_RELATIVE_PATH
40
- ];
41
-
42
- for (const relativePath of managedRelativePaths) {
41
+ for (const relativePath of MANAGED_MOBILE_FILE_RELATIVE_PATHS) {
43
42
  const absolutePath = pathModule.join(appRoot, relativePath);
44
43
  if (!(await fileExists(absolutePath))) {
45
44
  continue;
@@ -60,70 +59,59 @@ async function collectManagedMobileFileDriftIssues({
60
59
  }
61
60
  if (currentContent !== expectedContent) {
62
61
  issues.push(
63
- `${normalizeRelativePath(appRoot, absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile sync android to refresh managed mobile-shell files.`
62
+ `${normalizeRelativePath(appRoot, absolutePath)} is stale and no longer matches config.mobile. Re-run jskit mobile android sync to refresh managed mobile-shell files.`
64
63
  );
65
64
  }
66
65
  }
67
66
  }
68
67
 
69
- async function collectMissingInstalledDependencyNames(ctx, appRoot = "", packageJson = {}) {
70
- const {
71
- fileExists,
72
- path: pathModule
73
- } = ctx;
74
- const sections = [
75
- packageJson?.dependencies,
76
- packageJson?.devDependencies,
77
- packageJson?.optionalDependencies
78
- ];
79
- const missing = [];
80
- const seen = new Set();
81
-
82
- for (const section of sections) {
83
- if (!section || typeof section !== "object" || Array.isArray(section)) {
84
- continue;
85
- }
86
-
87
- for (const packageName of Object.keys(section).sort((left, right) => left.localeCompare(right))) {
88
- const normalizedPackageName = String(packageName || "").trim();
89
- if (!normalizedPackageName || seen.has(normalizedPackageName)) {
90
- continue;
91
- }
92
- seen.add(normalizedPackageName);
93
-
94
- const packageJsonPath = pathModule.join(
95
- appRoot,
96
- "node_modules",
97
- ...normalizedPackageName.split("/"),
98
- "package.json"
68
+ function renderAndroidMobileCommandList(lines, color) {
69
+ for (const entry of listMobileCommandDefinitions()) {
70
+ if (entry.name === "dev") {
71
+ lines.push(
72
+ ` - ${color.item(entry.name)}: Shortcut to run ${color.emphasis("sync")}, ${color.emphasis("tunnel")}, ${color.emphasis("run")} in this order`
99
73
  );
100
- if (!(await fileExists(packageJsonPath))) {
101
- missing.push(normalizedPackageName);
102
- }
74
+ continue;
103
75
  }
76
+ lines.push(` - ${color.item(entry.name)}: ${entry.summary}`);
104
77
  }
105
-
106
- return missing;
107
78
  }
108
79
 
109
- function renderMobileHelp(stream, definition = null) {
80
+ function renderMobileHelp(stream, definition = null, platform = "") {
110
81
  const color = createColorFormatter(stream);
111
82
  const lines = [];
112
83
 
113
- if (!definition) {
84
+ if (!definition && !platform) {
114
85
  lines.push(`Command: ${color.emphasis("mobile")}`);
115
86
  lines.push("");
116
87
  lines.push(color.heading("1) Minimal use"));
117
- lines.push(" jskit mobile <subcommand>");
88
+ lines.push(" jskit mobile <platform> <subcommand>");
118
89
  lines.push("");
119
- lines.push(color.heading("2) Subcommands"));
120
- for (const entry of listMobileCommandDefinitions()) {
121
- lines.push(` - ${color.item(entry.name)}: ${entry.summary}`);
122
- }
90
+ lines.push(color.heading("2) Platforms"));
91
+ lines.push(` - ${color.item("android")}`);
92
+ renderAndroidMobileCommandList(lines, color);
123
93
  lines.push("");
124
94
  lines.push(color.heading("3) Notes"));
125
95
  lines.push(" - Mobile helpers are for the Stage 1 Android Capacitor shell flow.");
126
- lines.push(" - Use jskit mobile <subcommand> help for subcommand-specific usage.");
96
+ lines.push(" - Use jskit mobile <platform> help for platform-specific usage.");
97
+ writeWrappedLines({
98
+ stdout: stream,
99
+ lines
100
+ });
101
+ return;
102
+ }
103
+
104
+ if (!definition) {
105
+ lines.push(`Mobile platform: ${color.emphasis(platform)}`);
106
+ lines.push("");
107
+ lines.push(color.heading("1) Minimal use"));
108
+ lines.push(` jskit mobile ${platform} <subcommand>`);
109
+ lines.push("");
110
+ lines.push(color.heading("2) Subcommands"));
111
+ renderAndroidMobileCommandList(lines, color);
112
+ lines.push("");
113
+ lines.push(color.heading("3) Notes"));
114
+ lines.push(` - Use jskit mobile ${platform} <subcommand> help for subcommand-specific usage.`);
127
115
  writeWrappedLines({
128
116
  stdout: stream,
129
117
  lines
@@ -256,7 +244,7 @@ function resolveAdbReversePort({
256
244
  const port = Number(parsedUrl.port || "");
257
245
  if (!Number.isInteger(port) || port < 1 || port > 65535) {
258
246
  throw createCliError(
259
- `config.mobile.apiBaseUrl "${apiBaseUrl}" must include an explicit port so jskit mobile tunnel android can infer adb reverse.`
247
+ `config.mobile.apiBaseUrl "${apiBaseUrl}" must include an explicit port so jskit mobile android tunnel can infer adb reverse.`
260
248
  );
261
249
  }
262
250
 
@@ -337,7 +325,7 @@ async function resolveAndroidDeviceTarget({
337
325
  appRoot
338
326
  });
339
327
  if (devices.length < 1) {
340
- throw ctx.createCliError(`No Android devices are visible to adb. Run jskit mobile devices android before ${commandLabel}.`);
328
+ throw ctx.createCliError(`No Android devices are visible to adb. Run jskit mobile android devices before ${commandLabel}.`);
341
329
  }
342
330
 
343
331
  const normalizedExplicitTarget = String(explicitTarget || "").trim();
@@ -346,7 +334,7 @@ async function resolveAndroidDeviceTarget({
346
334
  : devices[0];
347
335
 
348
336
  if (!selectedDevice) {
349
- throw ctx.createCliError(`Android device "${normalizedExplicitTarget}" is not visible to adb. Run jskit mobile devices android first.`);
337
+ throw ctx.createCliError(`Android device "${normalizedExplicitTarget}" is not visible to adb. Run jskit mobile android devices first.`);
350
338
  }
351
339
  if (selectedDevice.state !== "device") {
352
340
  throw ctx.createCliError(`Android device "${selectedDevice.serial}" is currently "${selectedDevice.state}", not ready for ${commandLabel}.`);
@@ -400,9 +388,13 @@ async function runLocalBinary(binaryName, args = [], {
400
388
 
401
389
  child.on("error", (error) => {
402
390
  if (error?.code === "ENOENT") {
391
+ const installHint =
392
+ binaryName === "cap"
393
+ ? ` Run npm install after adding ${CAPACITOR_RUNTIME_PACKAGE_ID}, then rerun this command.`
394
+ : "";
403
395
  reject(
404
396
  createCliError(
405
- `Could not find local "${binaryName}" in node_modules/.bin. Re-run jskit mobile add capacitor after npm install succeeds.`
397
+ `Could not find local "${binaryName}" in node_modules/.bin.${installHint}`
406
398
  )
407
399
  );
408
400
  return;
@@ -422,122 +414,93 @@ async function runLocalBinary(binaryName, args = [], {
422
414
  });
423
415
  }
424
416
 
425
- async function runMobileAppInstall({
417
+ function hasPackageDependency(packageJson = {}, packageId = "") {
418
+ const sections = [
419
+ packageJson?.dependencies,
420
+ packageJson?.devDependencies,
421
+ packageJson?.optionalDependencies
422
+ ];
423
+ return sections.some((section) => (
424
+ section &&
425
+ typeof section === "object" &&
426
+ !Array.isArray(section) &&
427
+ Object.prototype.hasOwnProperty.call(section, packageId)
428
+ ));
429
+ }
430
+
431
+ async function readJsonFileForMobileCommand(filePath = "", label = "", createCliError) {
432
+ try {
433
+ return JSON.parse(await readFile(filePath, "utf8"));
434
+ } catch (error) {
435
+ const message = String(error?.message || error || "unknown error");
436
+ throw createCliError(`Could not read ${label}: ${message}`);
437
+ }
438
+ }
439
+
440
+ async function assertMobileRuntimePackageInstalled({
426
441
  ctx,
427
- appRoot,
428
- stdout,
429
- stderr,
430
- dryRun = false,
431
- devlinks = false
442
+ appRoot
432
443
  } = {}) {
433
444
  const {
434
445
  path: pathModule,
435
- loadAppPackageJson
446
+ createCliError
436
447
  } = ctx;
437
- const { packageJson } = await loadAppPackageJson(appRoot);
438
- const packageScripts = packageJson?.scripts && typeof packageJson.scripts === "object" ? packageJson.scripts : {};
439
-
440
- await runLocalBinary("npm", ["install"], {
441
- appRoot,
442
- stderr,
443
- stdout,
444
- pathModule,
445
- createCliError: ctx.createCliError,
446
- dryRun
447
- });
448
+ const packageJsonPath = pathModule.join(appRoot, "package.json");
449
+ const lockPath = pathModule.join(appRoot, ".jskit", "lock.json");
450
+ const packageJson = await readJsonFileForMobileCommand(packageJsonPath, "package.json", createCliError);
451
+ const lock = await readJsonFileForMobileCommand(lockPath, ".jskit/lock.json", createCliError);
452
+ const hasDependency = hasPackageDependency(packageJson, CAPACITOR_RUNTIME_PACKAGE_ID);
453
+ const hasLockRecord = Boolean(lock?.installedPackages?.[CAPACITOR_RUNTIME_PACKAGE_ID]);
448
454
 
449
- if (devlinks === true && Object.prototype.hasOwnProperty.call(packageScripts, "devlinks")) {
450
- await runLocalBinary("npm", ["run", "--if-present", "devlinks"], {
451
- appRoot,
452
- stderr,
453
- stdout,
454
- pathModule,
455
- createCliError: ctx.createCliError,
456
- dryRun
457
- });
455
+ if (!hasDependency || !hasLockRecord) {
456
+ throw createCliError(
457
+ `Mobile Capacitor runtime package is not installed for this app. Run jskit add package ${CAPACITOR_RUNTIME_PACKAGE_ID} first.`
458
+ );
458
459
  }
459
460
  }
460
461
 
461
462
  async function refreshManagedMobileFiles({
462
463
  ctx,
463
- commandAdd,
464
464
  appRoot,
465
465
  options = {},
466
- stdout,
467
- stderr
466
+ stdout
468
467
  } = {}) {
469
468
  const {
470
- path: pathModule
469
+ fileExists,
470
+ path: pathModule,
471
+ normalizeRelativePath,
472
+ createCliError
471
473
  } = ctx;
472
- const packageJsonPath = pathModule.join(appRoot, "package.json");
473
- const packageJsonBefore = await readFile(packageJsonPath, "utf8");
474
- let capturedStdout = "";
475
- await commandAdd({
476
- positional: ["package", CAPACITOR_RUNTIME_PACKAGE_ID],
477
- options: {
478
- ...options,
479
- forceReapplyTarget: true,
480
- runNpmInstall: false,
481
- inlineOptions: {}
482
- },
483
- cwd: appRoot,
484
- io: {
485
- stdout: {
486
- write(chunk) {
487
- capturedStdout += String(chunk || "");
488
- }
489
- },
490
- stderr
491
- }
492
- });
493
- const packageJsonAfter = await readFile(packageJsonPath, "utf8");
494
- const parsedPackageJsonAfter = JSON.parse(packageJsonAfter);
495
- const missingInstalledDependencies = await collectMissingInstalledDependencyNames(ctx, appRoot, parsedPackageJsonAfter);
496
474
 
497
- if (!/Touched files \(0\):/u.test(capturedStdout)) {
498
- stdout.write(capturedStdout);
499
- }
475
+ for (const relativePath of MANAGED_MOBILE_FILE_RELATIVE_PATHS) {
476
+ const absolutePath = pathModule.join(appRoot, relativePath);
477
+ if (!(await fileExists(absolutePath))) {
478
+ throw createCliError(
479
+ `Managed mobile file is missing: ${normalizeRelativePath(appRoot, absolutePath)}. Run jskit add package ${CAPACITOR_RUNTIME_PACKAGE_ID} first.`
480
+ );
481
+ }
500
482
 
501
- if (
502
- options?.dryRun !== true &&
503
- (packageJsonAfter !== packageJsonBefore || missingInstalledDependencies.length > 0)
504
- ) {
505
- await runMobileAppInstall({
506
- ctx,
483
+ const currentSource = await readFile(absolutePath, "utf8");
484
+ const nextSource = await renderManagedMobileFile({
507
485
  appRoot,
508
- stdout,
509
- stderr,
510
- dryRun: false,
511
- devlinks: options?.devlinks === true
486
+ relativeTargetPath: relativePath
512
487
  });
513
- }
514
- }
488
+ if (nextSource === currentSource) {
489
+ continue;
490
+ }
515
491
 
516
- async function runMobileAddCapacitorCommand({
517
- commandAdd,
518
- appRoot,
519
- options = {},
520
- stdout,
521
- stderr
522
- }) {
523
- return await commandAdd({
524
- positional: ["package", CAPACITOR_RUNTIME_PACKAGE_ID],
525
- options: {
526
- ...options,
527
- runNpmInstall: true,
528
- inlineOptions: {}
529
- },
530
- cwd: appRoot,
531
- io: {
532
- stdout,
533
- stderr
492
+ if (options?.dryRun === true) {
493
+ stdout?.write(`[dry-run] refresh ${normalizeRelativePath(appRoot, absolutePath)}\n`);
494
+ continue;
534
495
  }
535
- });
496
+
497
+ await writeFile(absolutePath, nextSource, "utf8");
498
+ stdout?.write(`[mobile] Refreshed ${normalizeRelativePath(appRoot, absolutePath)}.\n`);
499
+ }
536
500
  }
537
501
 
538
502
  async function runMobileSyncAndroidCommand({
539
503
  ctx,
540
- commandAdd,
541
504
  appRoot,
542
505
  options = {},
543
506
  stdout,
@@ -547,19 +510,20 @@ async function runMobileSyncAndroidCommand({
547
510
  path: pathModule
548
511
  } = ctx;
549
512
 
550
- await refreshManagedMobileFiles({
513
+ await assertMobileRuntimePackageInstalled({
551
514
  ctx,
552
- commandAdd,
553
- appRoot,
554
- options,
555
- stdout,
556
- stderr
515
+ appRoot
557
516
  });
558
-
559
517
  await assertCapacitorShellInstalled({
560
518
  ctx,
561
519
  appRoot
562
520
  });
521
+ await refreshManagedMobileFiles({
522
+ ctx,
523
+ appRoot,
524
+ options,
525
+ stdout
526
+ });
563
527
  await ensureAndroidNativeShellIdentity({
564
528
  ctx,
565
529
  appRoot,
@@ -600,7 +564,6 @@ async function runMobileSyncAndroidCommand({
600
564
 
601
565
  async function runMobileRunAndroidCommand({
602
566
  ctx,
603
- commandAdd,
604
567
  appRoot,
605
568
  options = {},
606
569
  stdout,
@@ -625,26 +588,26 @@ async function runMobileRunAndroidCommand({
625
588
  if (mobileConfig.assetMode === "bundled") {
626
589
  await runMobileSyncAndroidCommand({
627
590
  ctx,
628
- commandAdd,
629
591
  appRoot,
630
592
  options,
631
593
  stdout,
632
594
  stderr
633
595
  });
634
596
  } else {
635
- await refreshManagedMobileFiles({
597
+ await assertMobileRuntimePackageInstalled({
636
598
  ctx,
637
- commandAdd,
638
- appRoot,
639
- options,
640
- stdout,
641
- stderr
599
+ appRoot
642
600
  });
643
-
644
601
  await assertCapacitorShellInstalled({
645
602
  ctx,
646
603
  appRoot
647
604
  });
605
+ await refreshManagedMobileFiles({
606
+ ctx,
607
+ appRoot,
608
+ options,
609
+ stdout
610
+ });
648
611
  await ensureAndroidNativeShellIdentity({
649
612
  ctx,
650
613
  appRoot,
@@ -715,7 +678,6 @@ async function runCapRunAndroidCommand({
715
678
 
716
679
  async function runMobileBuildAndroidCommand({
717
680
  ctx,
718
- commandAdd,
719
681
  appRoot,
720
682
  options = {},
721
683
  stdout,
@@ -738,13 +700,12 @@ async function runMobileBuildAndroidCommand({
738
700
 
739
701
  if (mobileConfig.assetMode !== "bundled") {
740
702
  throw createCliError(
741
- 'jskit mobile build android requires config.mobile.assetMode="bundled" so the release shell does not depend on a live dev server.'
703
+ 'jskit mobile android build requires config.mobile.assetMode="bundled" so the release shell does not depend on a live dev server.'
742
704
  );
743
705
  }
744
706
 
745
707
  await runMobileSyncAndroidCommand({
746
708
  ctx,
747
- commandAdd,
748
709
  appRoot,
749
710
  options,
750
711
  stdout,
@@ -906,9 +867,6 @@ async function runMobileTunnelAndroidCommand({
906
867
  }) {
907
868
  const inlineOptions = normalizeInlineOptions(options);
908
869
  const target = String(inlineOptions.target || "").trim();
909
- if (!target) {
910
- throw ctx.createCliError("jskit mobile tunnel android requires --target <device-id>.");
911
- }
912
870
 
913
871
  const mobileConfig = await resolveInstalledMobileConfigForCommand({
914
872
  appRoot,
@@ -960,9 +918,6 @@ async function runMobileRestartAndroidCommand({
960
918
  }) {
961
919
  const inlineOptions = normalizeInlineOptions(options);
962
920
  const target = String(inlineOptions.target || "").trim();
963
- if (!target) {
964
- throw ctx.createCliError("jskit mobile restart android requires --target <device-id>.");
965
- }
966
921
 
967
922
  const mobileConfig = await resolveInstalledMobileConfigForCommand({
968
923
  appRoot,
@@ -1004,7 +959,6 @@ async function runMobileRestartAndroidCommand({
1004
959
 
1005
960
  async function runMobileDevAndroidCommand({
1006
961
  ctx,
1007
- commandAdd,
1008
962
  appRoot,
1009
963
  options = {},
1010
964
  stdout,
@@ -1020,30 +974,17 @@ async function runMobileDevAndroidCommand({
1020
974
 
1021
975
  stdout.write(`[mobile] Using Android device: ${selectedDevice.serial}\n`);
1022
976
  stdout.write("[mobile] Building and syncing the Android shell:\n");
1023
- stdout.write("[mobile] npx jskit mobile sync android\n");
977
+ stdout.write("[mobile] npx jskit mobile android sync\n");
1024
978
  await runMobileSyncAndroidCommand({
1025
979
  ctx,
1026
- commandAdd,
1027
980
  appRoot,
1028
981
  options,
1029
982
  stdout,
1030
983
  stderr
1031
984
  });
1032
985
 
1033
- stdout.write(`[mobile] Installing and launching the app on ${selectedDevice.serial}:\n`);
1034
- stdout.write(`[mobile] npx jskit mobile run android --target ${selectedDevice.serial}\n`);
1035
- await runCapRunAndroidCommand({
1036
- ctx,
1037
- appRoot,
1038
- pathModule: ctx.path,
1039
- target: selectedDevice.serial,
1040
- stdout,
1041
- stderr,
1042
- dryRun: false
1043
- });
1044
-
1045
986
  stdout.write(`[mobile] Creating the adb reverse tunnel on ${selectedDevice.serial}:\n`);
1046
- stdout.write(`[mobile] npx jskit mobile tunnel android --target ${selectedDevice.serial}\n`);
987
+ stdout.write(`[mobile] npx jskit mobile android tunnel --target ${selectedDevice.serial}\n`);
1047
988
  await runMobileTunnelAndroidCommand({
1048
989
  ctx,
1049
990
  appRoot,
@@ -1056,23 +997,32 @@ async function runMobileDevAndroidCommand({
1056
997
  stderr
1057
998
  });
1058
999
 
1000
+ stdout.write(`[mobile] Installing and launching the app on ${selectedDevice.serial}:\n`);
1001
+ stdout.write(`[mobile] npx jskit mobile android run --target ${selectedDevice.serial}\n`);
1002
+ await runCapRunAndroidCommand({
1003
+ ctx,
1004
+ appRoot,
1005
+ pathModule: ctx.path,
1006
+ target: selectedDevice.serial,
1007
+ stdout,
1008
+ stderr,
1009
+ dryRun: false
1010
+ });
1011
+
1059
1012
  return 0;
1060
1013
  }
1061
1014
 
1062
- function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1015
+ function createMobileCommands(ctx = {}) {
1063
1016
  const {
1064
1017
  createCliError,
1065
1018
  resolveAppRootFromCwd
1066
1019
  } = ctx;
1067
1020
 
1068
- if (typeof commandAdd !== "function") {
1069
- throw new TypeError("createMobileCommands requires commandAdd().");
1070
- }
1071
-
1072
1021
  async function commandMobile({ positional = [], options = {}, cwd = "", stdout, stderr }) {
1073
1022
  const firstToken = String(positional[0] || "").trim();
1074
1023
  const secondToken = String(positional[1] || "").trim();
1075
- const remainingPositionals = positional.slice(2);
1024
+ const thirdToken = String(positional[2] || "").trim();
1025
+ const remainingPositionals = positional.slice(3);
1076
1026
 
1077
1027
  if (!firstToken) {
1078
1028
  renderMobileHelp(stdout);
@@ -1080,19 +1030,31 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1080
1030
  }
1081
1031
 
1082
1032
  if (firstToken === "help") {
1083
- renderMobileHelp(stdout, resolveMobileCommandDefinition(secondToken));
1033
+ renderMobileHelp(stdout);
1084
1034
  return 0;
1085
1035
  }
1086
1036
 
1087
- const definition = resolveMobileCommandDefinition(firstToken);
1088
- if (!definition) {
1089
- throw createCliError(`Unknown mobile subcommand: ${firstToken}.`, {
1037
+ const platform = firstToken;
1038
+ if (platform !== "android") {
1039
+ throw createCliError(`Unknown mobile platform: ${platform}.`, {
1090
1040
  renderUsage: () => renderMobileHelp(stderr)
1091
1041
  });
1092
1042
  }
1093
1043
 
1094
- if (secondToken === "help") {
1095
- renderMobileHelp(stdout, definition);
1044
+ if (!secondToken || secondToken === "help") {
1045
+ renderMobileHelp(stdout, null, platform);
1046
+ return 0;
1047
+ }
1048
+
1049
+ const definition = resolveMobileCommandDefinition(secondToken);
1050
+ if (!definition) {
1051
+ throw createCliError(`Unknown mobile ${platform} subcommand: ${secondToken}.`, {
1052
+ renderUsage: () => renderMobileHelp(stderr, null, platform)
1053
+ });
1054
+ }
1055
+
1056
+ if (thirdToken === "help") {
1057
+ renderMobileHelp(stdout, definition, platform);
1096
1058
  return 0;
1097
1059
  }
1098
1060
 
@@ -1102,14 +1064,14 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1102
1064
  const unknownInlineOptionNames = inlineOptionNames.filter((optionName) => !supportedOptionNames.has(optionName));
1103
1065
  if (unknownInlineOptionNames.length > 0) {
1104
1066
  throw createCliError(
1105
- `Unknown option${unknownInlineOptionNames.length === 1 ? "" : "s"} for jskit mobile ${definition.name}: ${unknownInlineOptionNames.map((optionName) => `--${optionName}`).join(", ")}.`,
1067
+ `Unknown option${unknownInlineOptionNames.length === 1 ? "" : "s"} for jskit mobile ${platform} ${definition.name}: ${unknownInlineOptionNames.map((optionName) => `--${optionName}`).join(", ")}.`,
1106
1068
  {
1107
1069
  renderUsage: () => renderMobileHelp(stderr, definition)
1108
1070
  }
1109
1071
  );
1110
1072
  }
1111
1073
  if (options?.dryRun === true && !supportedOptionNames.has("dry-run")) {
1112
- throw createCliError(`Unknown option for jskit mobile ${definition.name}: --dry-run.`, {
1074
+ throw createCliError(`Unknown option for jskit mobile ${platform} ${definition.name}: --dry-run.`, {
1113
1075
  renderUsage: () => renderMobileHelp(stderr, definition)
1114
1076
  });
1115
1077
  }
@@ -1117,13 +1079,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1117
1079
  const appRoot = await resolveAppRootFromCwd(cwd);
1118
1080
 
1119
1081
  if (definition.name === "devices") {
1120
- if (secondToken !== "android") {
1121
- throw createCliError(`jskit mobile devices currently supports only "android".`, {
1122
- renderUsage: () => renderMobileHelp(stderr, definition)
1123
- });
1124
- }
1125
- if (remainingPositionals.length > 0) {
1126
- throw createCliError(`Unexpected positional arguments for jskit mobile devices: ${remainingPositionals.join(" ")}`, {
1082
+ if (thirdToken || remainingPositionals.length > 0) {
1083
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} devices: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1127
1084
  renderUsage: () => renderMobileHelp(stderr, definition)
1128
1085
  });
1129
1086
  }
@@ -1137,20 +1094,14 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1137
1094
  }
1138
1095
 
1139
1096
  if (definition.name === "dev") {
1140
- if (secondToken !== "android") {
1141
- throw createCliError(`jskit mobile dev currently supports only "android".`, {
1142
- renderUsage: () => renderMobileHelp(stderr, definition)
1143
- });
1144
- }
1145
- if (remainingPositionals.length > 0) {
1146
- throw createCliError(`Unexpected positional arguments for jskit mobile dev: ${remainingPositionals.join(" ")}`, {
1097
+ if (thirdToken || remainingPositionals.length > 0) {
1098
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} dev: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1147
1099
  renderUsage: () => renderMobileHelp(stderr, definition)
1148
1100
  });
1149
1101
  }
1150
1102
 
1151
1103
  return runMobileDevAndroidCommand({
1152
1104
  ctx,
1153
- commandAdd,
1154
1105
  appRoot,
1155
1106
  options,
1156
1107
  stdout,
@@ -1159,13 +1110,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1159
1110
  }
1160
1111
 
1161
1112
  if (definition.name === "tunnel") {
1162
- if (secondToken !== "android") {
1163
- throw createCliError(`jskit mobile tunnel currently supports only "android".`, {
1164
- renderUsage: () => renderMobileHelp(stderr, definition)
1165
- });
1166
- }
1167
- if (remainingPositionals.length > 0) {
1168
- throw createCliError(`Unexpected positional arguments for jskit mobile tunnel: ${remainingPositionals.join(" ")}`, {
1113
+ if (thirdToken || remainingPositionals.length > 0) {
1114
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} tunnel: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1169
1115
  renderUsage: () => renderMobileHelp(stderr, definition)
1170
1116
  });
1171
1117
  }
@@ -1180,13 +1126,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1180
1126
  }
1181
1127
 
1182
1128
  if (definition.name === "restart") {
1183
- if (secondToken !== "android") {
1184
- throw createCliError(`jskit mobile restart currently supports only "android".`, {
1185
- renderUsage: () => renderMobileHelp(stderr, definition)
1186
- });
1187
- }
1188
- if (remainingPositionals.length > 0) {
1189
- throw createCliError(`Unexpected positional arguments for jskit mobile restart: ${remainingPositionals.join(" ")}`, {
1129
+ if (thirdToken || remainingPositionals.length > 0) {
1130
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} restart: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1190
1131
  renderUsage: () => renderMobileHelp(stderr, definition)
1191
1132
  });
1192
1133
  }
@@ -1200,43 +1141,15 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1200
1141
  });
1201
1142
  }
1202
1143
 
1203
- if (definition.name === "add") {
1204
- if (secondToken !== "capacitor") {
1205
- throw createCliError(`jskit mobile add currently supports only "capacitor".`, {
1206
- renderUsage: () => renderMobileHelp(stderr, definition)
1207
- });
1208
- }
1209
- if (remainingPositionals.length > 0) {
1210
- throw createCliError(`Unexpected positional arguments for jskit mobile add: ${remainingPositionals.join(" ")}`, {
1211
- renderUsage: () => renderMobileHelp(stderr, definition)
1212
- });
1213
- }
1214
-
1215
- return runMobileAddCapacitorCommand({
1216
- ctx,
1217
- commandAdd,
1218
- appRoot,
1219
- options,
1220
- stdout,
1221
- stderr
1222
- });
1223
- }
1224
-
1225
1144
  if (definition.name === "sync") {
1226
- if (secondToken !== "android") {
1227
- throw createCliError(`jskit mobile sync currently supports only "android".`, {
1228
- renderUsage: () => renderMobileHelp(stderr, definition)
1229
- });
1230
- }
1231
- if (remainingPositionals.length > 0) {
1232
- throw createCliError(`Unexpected positional arguments for jskit mobile sync: ${remainingPositionals.join(" ")}`, {
1145
+ if (thirdToken || remainingPositionals.length > 0) {
1146
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} sync: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1233
1147
  renderUsage: () => renderMobileHelp(stderr, definition)
1234
1148
  });
1235
1149
  }
1236
1150
 
1237
1151
  return runMobileSyncAndroidCommand({
1238
1152
  ctx,
1239
- commandAdd,
1240
1153
  appRoot,
1241
1154
  options,
1242
1155
  stdout,
@@ -1245,20 +1158,14 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1245
1158
  }
1246
1159
 
1247
1160
  if (definition.name === "run") {
1248
- if (secondToken !== "android") {
1249
- throw createCliError(`jskit mobile run currently supports only "android".`, {
1250
- renderUsage: () => renderMobileHelp(stderr, definition)
1251
- });
1252
- }
1253
- if (remainingPositionals.length > 0) {
1254
- throw createCliError(`Unexpected positional arguments for jskit mobile run: ${remainingPositionals.join(" ")}`, {
1161
+ if (thirdToken || remainingPositionals.length > 0) {
1162
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} run: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1255
1163
  renderUsage: () => renderMobileHelp(stderr, definition)
1256
1164
  });
1257
1165
  }
1258
1166
 
1259
1167
  return runMobileRunAndroidCommand({
1260
1168
  ctx,
1261
- commandAdd,
1262
1169
  appRoot,
1263
1170
  options,
1264
1171
  stdout,
@@ -1267,20 +1174,14 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1267
1174
  }
1268
1175
 
1269
1176
  if (definition.name === "build") {
1270
- if (secondToken !== "android") {
1271
- throw createCliError(`jskit mobile build currently supports only "android".`, {
1272
- renderUsage: () => renderMobileHelp(stderr, definition)
1273
- });
1274
- }
1275
- if (remainingPositionals.length > 0) {
1276
- throw createCliError(`Unexpected positional arguments for jskit mobile build: ${remainingPositionals.join(" ")}`, {
1177
+ if (thirdToken || remainingPositionals.length > 0) {
1178
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} build: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1277
1179
  renderUsage: () => renderMobileHelp(stderr, definition)
1278
1180
  });
1279
1181
  }
1280
1182
 
1281
1183
  return runMobileBuildAndroidCommand({
1282
1184
  ctx,
1283
- commandAdd,
1284
1185
  appRoot,
1285
1186
  options,
1286
1187
  stdout,
@@ -1289,8 +1190,8 @@ function createMobileCommands(ctx = {}, { commandAdd } = {}) {
1289
1190
  }
1290
1191
 
1291
1192
  if (definition.name === "doctor") {
1292
- if (secondToken) {
1293
- throw createCliError(`Unexpected positional arguments for jskit mobile doctor: ${[secondToken, ...remainingPositionals].join(" ")}`, {
1193
+ if (thirdToken || remainingPositionals.length > 0) {
1194
+ throw createCliError(`Unexpected positional arguments for jskit mobile ${platform} doctor: ${[thirdToken, ...remainingPositionals].filter(Boolean).join(" ")}`, {
1294
1195
  renderUsage: () => renderMobileHelp(stderr, definition)
1295
1196
  });
1296
1197
  }