Skip to content

Commit 4c5db77

Browse files
fix: merge main
2 parents 13536ba + a3e56bf commit 4c5db77

File tree

12 files changed

+1953
-1457
lines changed

12 files changed

+1953
-1457
lines changed

messages/device-start.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@ starting device %s
2424

2525
# device.start.successStatus.android
2626

27-
device %s started on port %s, writable system = %s
27+
device '%s' started on port %s, writable system = %s
2828

2929
# device.start.successStatus.ios
3030

31-
device %s started
31+
device '%s' started
3232

3333
# examples
3434

35-
- <%= config.bin %> <%= command.id %> -p ios -t 'iPhone 16'
36-
- <%= config.bin %> <%= command.id %> -p ios -t '3627EBD5-E9EC-4EC4-8E89-C6A0232C920D'
37-
- <%= config.bin %> <%= command.id %> -p android -t 'Pixel 9 API 35' -w
35+
- <%= config.bin %> <%= command.id %> -p ios -t 'iPhone 16'
36+
- <%= config.bin %> <%= command.id %> -p ios -t '3627EBD5-E9EC-4EC4-8E89-C6A0232C920D'
37+
- <%= config.bin %> <%= command.id %> -p android -t 'Pixel 9 API 35' -w

package.json

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@salesforce/lwc-dev-mobile",
33
"description": "Salesforce CLI plugin for mobile extensions to Local Development",
4-
"version": "3.0.0-alpha.1",
4+
"version": "3.0.0-alpha.2",
55
"author": {
66
"name": "Meisam Seyed Aliroteh",
77
"email": "maliroteh@salesforce.com",
@@ -36,12 +36,13 @@
3636
],
3737
"bugs": "https://github.com/forcedotcom/lwc-dev-mobile/issues",
3838
"dependencies": {
39-
"@oclif/core": "^4.4.0",
40-
"@salesforce/core": "^8.15.0",
41-
"@salesforce/lwc-dev-mobile-core": "^4.0.0-alpha.13",
42-
"@salesforce/sf-plugins-core": "^12.2.3",
39+
"@oclif/core": "^4.8.0",
40+
"@salesforce/core": "^8.23.4",
41+
"@salesforce/lwc-dev-mobile-core": "^4.0.0-alpha.14",
42+
"@salesforce/sf-plugins-core": "^12.2.5",
4343
"archy": "^1.0.0",
44-
"chalk": "^5.4.1"
44+
"chalk": "^5.6.2",
45+
"zod": "^4.1.12"
4546
},
4647
"devDependencies": {
4748
"@oclif/plugin-command-snapshot": "^5.3.2",

src/cli/commands/force/lightning/local/device/create.ts

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ import {
2323
Requirement,
2424
RequirementProcessor
2525
} from '@salesforce/lwc-dev-mobile-core';
26+
import { z } from 'zod';
27+
import { DeviceOperationResultSchema, DeviceOperationResultType, DeviceSchema } from '../schema/device.js';
2628

2729
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2830
const messages = Messages.loadMessages('@salesforce/lwc-dev-mobile', 'device-create');
@@ -34,6 +36,7 @@ export class Create extends BaseCommand {
3436
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
3537
public static readonly flags = {
3638
...CommandLineUtils.createFlag(FlagsConfigType.JsonFlag, false),
39+
...CommandLineUtils.createFlag(FlagsConfigType.OutputFormatFlag, false),
3740
...CommandLineUtils.createFlag(FlagsConfigType.LogLevelFlag, false),
3841
...CommandLineUtils.createFlag(FlagsConfigType.ApiLevelFlag, false),
3942
...CommandLineUtils.createFlag(FlagsConfigType.PlatformFlag, true),
@@ -70,28 +73,58 @@ export class Create extends BaseCommand {
7073
return this.flagValues.apilevel as string | undefined;
7174
}
7275

73-
public async run(): Promise<void> {
76+
protected static getOutputSchema(): z.ZodTypeAny {
77+
return DeviceOperationResultSchema;
78+
}
79+
80+
public async run(): Promise<DeviceOperationResultType | void> {
7481
this.logger.info(`Device Create command invoked for ${this.platform}`);
7582

76-
return RequirementProcessor.execute(this.commandRequirements)
77-
.then(() => {
78-
// execute the create command
83+
const creationSuccessMessage = messages.getMessage('device.create.successStatus', [
84+
this.deviceName,
85+
this.deviceType
86+
]);
87+
88+
try {
89+
// if not in JSON mode, execute the requirements and start the CLI action
90+
if (!this.jsonEnabled()) {
91+
await RequirementProcessor.execute(this.commandRequirements);
7992
this.logger.info('Setup requirements met, continuing with Device Create');
93+
8094
CommonUtils.startCliAction(
8195
messages.getMessage('device.create.action'),
8296
messages.getMessage('device.create.status', [this.deviceName, this.deviceType])
8397
);
84-
return this.executeDeviceCreate();
85-
})
86-
.then(() => {
87-
const message = messages.getMessage('device.create.successStatus', [this.deviceName, this.deviceType]);
88-
CommonUtils.stopCliAction(message);
89-
})
90-
.catch((error: Error) => {
98+
}
99+
100+
// execute the device create command
101+
await this.executeDeviceCreate();
102+
103+
if (this.jsonEnabled()) {
104+
// In JSON mode, get the device and return it in the format of the DeviceSchema
105+
const device = await (CommandLineUtils.platformFlagIsAndroid(this.platform)
106+
? new AndroidDeviceManager()
107+
: new AppleDeviceManager()
108+
).getDevice(this.deviceName);
109+
110+
const deviceInfo = DeviceSchema.parse(device);
111+
return {
112+
device: deviceInfo,
113+
success: true,
114+
message: creationSuccessMessage
115+
};
116+
} else {
117+
// if cli mode, stop the CLI action
118+
CommonUtils.stopCliAction(creationSuccessMessage);
119+
}
120+
} catch (error) {
121+
if (!this.jsonEnabled()) {
122+
// if cli mode, stop the CLI action
91123
CommonUtils.stopCliAction(messages.getMessage('device.create.failureStatus'));
92-
this.logger.warn(`Device Create failed for ${this.platform} - ${error.message}`);
93-
return Promise.reject(error);
94-
});
124+
}
125+
this.logger.warn(`Device Create failed for ${this.platform} - ${(error as Error).message}`);
126+
throw error;
127+
}
95128
}
96129

97130
protected populateCommandRequirements(): void {

src/cli/commands/force/lightning/local/device/list.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import chalk from 'chalk';
99
import archy from 'archy';
1010
import { Messages } from '@salesforce/core';
11+
import { z } from 'zod';
12+
1113
import {
1214
AndroidDevice,
1315
AndroidDeviceManager,
@@ -19,6 +21,7 @@ import {
1921
FlagsConfigType,
2022
PerformanceMarkers
2123
} from '@salesforce/lwc-dev-mobile-core';
24+
import { DeviceListSchema } from '../schema/device.js';
2225

2326
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2427
const messages = Messages.loadMessages('@salesforce/lwc-dev-mobile', 'device-list');
@@ -30,6 +33,7 @@ export class List extends BaseCommand {
3033
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
3134
public static readonly flags = {
3235
...CommandLineUtils.createFlag(FlagsConfigType.JsonFlag, false),
36+
...CommandLineUtils.createFlag(FlagsConfigType.OutputFormatFlag, false),
3337
...CommandLineUtils.createFlag(FlagsConfigType.LogLevelFlag, false),
3438
...CommandLineUtils.createFlag(FlagsConfigType.PlatformFlag, true)
3539
};
@@ -43,36 +47,32 @@ export class List extends BaseCommand {
4347
return this.flagValues.platform as string;
4448
}
4549

50+
protected static getOutputSchema(): z.ZodTypeAny {
51+
return DeviceListSchema;
52+
}
53+
4654
public async run(): Promise<AndroidDevice[] | AppleDevice[]> {
4755
this.logger.info(`Device List command invoked for ${this.platform}`);
4856

49-
return CommandLineUtils.platformFlagIsIOS(this.platform) ? this.iOSDeviceList() : this.androidDeviceList();
50-
}
51-
52-
private async iOSDeviceList(): Promise<AppleDevice[]> {
53-
CommonUtils.startCliAction(
54-
messages.getMessage('device.list.action'),
55-
messages.getMessage('device.list.status')
56-
);
57+
if (!this.jsonEnabled()) {
58+
CommonUtils.startCliAction(
59+
messages.getMessage('device.list.action'),
60+
messages.getMessage('device.list.status')
61+
);
62+
}
5763
performance.mark(this.perfMarker.startMarkName);
58-
const devices = await new AppleDeviceManager().enumerateDevices();
59-
performance.mark(this.perfMarker.endMarkName);
60-
CommonUtils.stopCliAction();
61-
this.showDeviceList(devices);
62-
return Promise.resolve(devices);
63-
}
6464

65-
private async androidDeviceList(): Promise<AndroidDevice[]> {
66-
CommonUtils.startCliAction(
67-
messages.getMessage('device.list.action'),
68-
messages.getMessage('device.list.status')
69-
);
70-
performance.mark(this.perfMarker.startMarkName);
71-
const devices = await new AndroidDeviceManager().enumerateDevices();
65+
const devices = CommandLineUtils.platformFlagIsAndroid(this.platform)
66+
? await new AndroidDeviceManager().enumerateDevices()
67+
: await new AppleDeviceManager().enumerateDevices();
68+
7269
performance.mark(this.perfMarker.endMarkName);
73-
CommonUtils.stopCliAction();
74-
this.showDeviceList(devices);
75-
return Promise.resolve(devices);
70+
if (!this.jsonEnabled()) {
71+
CommonUtils.stopCliAction();
72+
this.showDeviceList(devices);
73+
}
74+
75+
return devices;
7676
}
7777

7878
private showDeviceList(devices: AndroidDevice[] | AppleDevice[]): void {

src/cli/commands/force/lightning/local/device/start.ts

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import {
2222
IOSEnvironmentRequirements,
2323
RequirementProcessor
2424
} from '@salesforce/lwc-dev-mobile-core';
25+
import { z } from 'zod';
26+
import { DeviceOperationResultSchema, DeviceOperationResultType, DeviceSchema } from '../schema/device.js';
2527

2628
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2729
const messages = Messages.loadMessages('@salesforce/lwc-dev-mobile', 'device-start');
@@ -33,6 +35,7 @@ export class Start extends BaseCommand {
3335
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
3436
public static readonly flags = {
3537
...CommandLineUtils.createFlag(FlagsConfigType.JsonFlag, false),
38+
...CommandLineUtils.createFlag(FlagsConfigType.OutputFormatFlag, false),
3639
...CommandLineUtils.createFlag(FlagsConfigType.LogLevelFlag, false),
3740
...CommandLineUtils.createFlag(FlagsConfigType.PlatformFlag, true),
3841
target: Flags.string({
@@ -64,10 +67,29 @@ export class Start extends BaseCommand {
6467
return this.flagValues.writablesystem as boolean;
6568
}
6669

67-
public async run(): Promise<void> {
70+
protected static getOutputSchema(): z.ZodTypeAny {
71+
return DeviceOperationResultSchema;
72+
}
73+
74+
public async run(): Promise<DeviceOperationResultType | void> {
6875
this.logger.info(`Device Start command invoked for ${this.platform}`);
6976

70-
return RequirementProcessor.execute(this.commandRequirements).then(() => {
77+
if (this.jsonEnabled()) {
78+
// if in JSON mode, just execute the start command and return the device
79+
const device = await this.executeDeviceStart();
80+
const deviceInfo = DeviceSchema.parse(device);
81+
const message = CommandLineUtils.platformFlagIsAndroid(this.platform)
82+
? this.getAndroidSuccessMessage(device as AndroidDevice)
83+
: this.getIOSSuccessMessage();
84+
return {
85+
device: deviceInfo,
86+
success: true,
87+
message
88+
};
89+
}
90+
91+
// do the RequirementProcessor.execute if not in JSON mode
92+
await RequirementProcessor.execute(this.commandRequirements).then(() => {
7193
// execute the start command
7294
this.logger.info('Setup requirements met, continuing with Device Start');
7395
return this.executeDeviceStart();
@@ -84,7 +106,7 @@ export class Start extends BaseCommand {
84106
this.commandRequirements = requirements;
85107
}
86108

87-
private async executeDeviceStart(): Promise<void> {
109+
private async executeDeviceStart(): Promise<AndroidDevice | AppleDevice> {
88110
const isAndroid = CommandLineUtils.platformFlagIsAndroid(this.platform);
89111

90112
const device = isAndroid
@@ -95,24 +117,38 @@ export class Start extends BaseCommand {
95117
return Promise.reject(messages.getMessage('error.target.doesNotExist', [this.target]));
96118
}
97119

98-
CommonUtils.startCliAction(
99-
messages.getMessage('device.start.action'),
100-
messages.getMessage('device.start.status', [this.target])
101-
);
120+
if (!this.jsonEnabled()) {
121+
CommonUtils.startCliAction(
122+
messages.getMessage('device.start.action'),
123+
messages.getMessage('device.start.status', [this.target])
124+
);
125+
}
102126

103127
if (isAndroid) {
104128
const avd = device as AndroidDevice;
105129
await avd.boot(true, this.writablesystem ? BootMode.systemWritableMandatory : BootMode.normal);
106-
CommonUtils.stopCliAction(
107-
messages.getMessage('device.start.successStatus.android', [
108-
this.target,
109-
avd.emulatorPort(),
110-
this.writablesystem
111-
])
112-
);
130+
if (!this.jsonEnabled()) {
131+
CommonUtils.stopCliAction(this.getAndroidSuccessMessage(device as AndroidDevice));
132+
}
113133
} else {
114134
await (device as AppleDevice).boot(true);
115-
CommonUtils.stopCliAction(messages.getMessage('device.start.successStatus.ios', [this.target]));
135+
if (!this.jsonEnabled()) {
136+
CommonUtils.stopCliAction(this.getIOSSuccessMessage());
137+
}
116138
}
139+
140+
return device;
141+
}
142+
143+
private getAndroidSuccessMessage(deviceInfo: AndroidDevice): string {
144+
return messages.getMessage('device.start.successStatus.android', [
145+
this.target,
146+
deviceInfo.emulatorPort(),
147+
this.writablesystem
148+
]);
149+
}
150+
151+
private getIOSSuccessMessage(): string {
152+
return messages.getMessage('device.start.successStatus.ios', [this.target]);
117153
}
118154
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: MIT
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6+
*/
7+
8+
import { z } from 'zod';
9+
10+
// Define the schema for the nested 'osVersion' object
11+
const OsVersionSchema = z.object({
12+
major: z.number(),
13+
minor: z.number(),
14+
patch: z.number()
15+
});
16+
17+
export const DeviceSchema = z.object({
18+
// Required fields present in boto ios and android devices
19+
id: z.string().describe('The ID of the device'),
20+
name: z.string().describe('The name of the device'),
21+
deviceType: z.string().describe('The type of the device'),
22+
osType: z.string().describe('The type of the operating system'),
23+
osVersion: z.union([z.string(), OsVersionSchema]).describe('The version of the operating system'),
24+
25+
// fields for Android devices
26+
isPlayStore: z.boolean().optional().describe('Whether the android device has google Play Store enabled'),
27+
port: z.number().optional().describe('The port number the android device is running on')
28+
});
29+
30+
// Define the main schema for the device
31+
export const DeviceListSchema = z.array(DeviceSchema);
32+
33+
export const DeviceOperationResultSchema = z.object({
34+
device: DeviceSchema.describe('The device that was operated on'),
35+
success: z.boolean().describe('Whether the operation was successful'),
36+
message: z.string().optional().describe('The message from the operation')
37+
});
38+
39+
export type DeviceOperationResultType = z.infer<typeof DeviceOperationResultSchema>;

src/cli/commands/force/lightning/local/setup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@
55
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
66
*/
77

8-
import { Setup as CoreSetup } from '@salesforce/lwc-dev-mobile-core';
8+
import { Setup as CoreSetup, RequirementCheckResultType } from '@salesforce/lwc-dev-mobile-core';
99

1010
export class Setup extends CoreSetup {
11-
public async run(): Promise<void> {
11+
public async run(): Promise<RequirementCheckResultType | void> {
1212
return super.run();
1313
}
1414
}

0 commit comments

Comments
 (0)