Skip to content

Commit 01d67f9

Browse files
fix: merge main
2 parents 85fcee6 + d89f4dd commit 01d67f9

File tree

20 files changed

+3226
-1357
lines changed

20 files changed

+3226
-1357
lines changed

.prettierignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Ignore files in the messages folder
2+
messages/
3+

messages/app-install.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# summary
2+
3+
Install the designated application onto the target virtual device
4+
5+
# flags.target.description
6+
7+
Target virtual device id or name.
8+
9+
# flags.appBundlePath.description
10+
11+
The file path of the app bundle to install.
12+
13+
# error.target.doesNotExist
14+
15+
Target device does not exist: %s
16+
17+
# app.install.action
18+
19+
App Install
20+
21+
# app.install.status
22+
23+
Installing '%s' onto '%s'
24+
25+
# app.install.successStatus
26+
27+
'%s' is installed onto '%s' successfully
28+
29+
# app.install.failureStatus
30+
31+
error encountered
32+
33+
# examples
34+
35+
- <%= config.bin %> <%= command.id %> -p ios -t MyNewVirtualDevice -a \users\bob\myapp\myApp.app
36+
- <%= config.bin %> <%= command.id %> -p android -t MyNewVirtualDevice -a \users\bob\myapp\myApp.apk

messages/app-launch.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# summary
2+
3+
Launch the designated application on the target virtual device.
4+
5+
# flags.target.description
6+
7+
Target virtual device id or name.
8+
9+
# flags.appBundlePath.description
10+
11+
App Bundle Path (for reinstallation).
12+
13+
# flags.bundleId.description
14+
15+
The application identifier. For iOS, use the Bundle ID (e.g., com.company.myapp). For Android, specify the package name along with the target activity name (e.g., com.company.myapp/.MainActivity).
16+
17+
# flags.launchArguments.description
18+
19+
A JSON array of objects, each containing a name and value string, to be passed as arguments to the application upon launch. Sample format: [{"name":"param1", "value":"value1"}].
20+
21+
# error.target.doesNotExist
22+
23+
Target device does not exist: %s
24+
25+
# app.launch.action
26+
27+
App Launch
28+
29+
# app.launch.status
30+
31+
Launching '%s' on '%s'
32+
33+
# app.launch.successStatus
34+
35+
'%s' is launched on '%s' successfully
36+
37+
# app.launch.failureStatus
38+
39+
error encountered
40+
41+
# examples
42+
43+
- <%= config.bin %> <%= command.id %> -p ios -t MyNewVirtualDevice -i com.company.app -a \users\bob\myapp\myApp.app
44+
- <%= config.bin %> <%= command.id %> -p android -t MyNewVirtualDevice -i com.company.app/.mainActivity -a \users\bob\myapp\myApp.apk

messages/device-start.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@ 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

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.5.4",
40-
"@salesforce/core": "^8.23.1",
41-
"@salesforce/lwc-dev-mobile-core": "^4.0.0-alpha.12",
42-
"@salesforce/sf-plugins-core": "^12.2.4",
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.6.2"
44+
"chalk": "^5.6.2",
45+
"zod": "^4.1.12"
4546
},
4647
"devDependencies": {
4748
"@oclif/plugin-command-snapshot": "^5.3.6",
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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 { Flags } from '@salesforce/sf-plugins-core';
9+
import { Messages } from '@salesforce/core';
10+
import {
11+
AndroidDeviceManager,
12+
AndroidEnvironmentRequirements,
13+
AppleDeviceManager,
14+
BaseCommand,
15+
CommandLineUtils,
16+
CommandRequirements,
17+
CommonUtils,
18+
FlagsConfigType,
19+
IOSEnvironmentRequirements,
20+
RequirementProcessor
21+
} from '@salesforce/lwc-dev-mobile-core';
22+
import { z } from 'zod';
23+
import { DeviceOperationResultSchema, DeviceOperationResultType, DeviceSchema } from '../schema/device.js';
24+
25+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
26+
const messages = Messages.loadMessages('@salesforce/lwc-dev-mobile', 'app-install');
27+
28+
export class Install extends BaseCommand {
29+
public static readonly summary = messages.getMessage('summary');
30+
public static readonly examples = messages.getMessages('examples');
31+
32+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
33+
public static readonly flags = {
34+
...CommandLineUtils.createFlag(FlagsConfigType.JsonFlag, false),
35+
...CommandLineUtils.createFlag(FlagsConfigType.OutputFormatFlag, false),
36+
...CommandLineUtils.createFlag(FlagsConfigType.LogLevelFlag, false),
37+
...CommandLineUtils.createFlag(FlagsConfigType.PlatformFlag, true),
38+
target: Flags.string({
39+
char: 't',
40+
description: messages.getMessage('flags.target.description'),
41+
required: true,
42+
validate: (val: string) => val && val.trim().length > 0
43+
}),
44+
appbundlepath: Flags.string({
45+
char: 'a',
46+
description: messages.getMessage('flags.appBundlePath.description'),
47+
required: true,
48+
validate: (val: string) => val && val.trim().length > 0
49+
})
50+
};
51+
52+
protected _commandName = 'force:lightning:local:app:install';
53+
54+
private get platform(): string {
55+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
56+
return this.flagValues.platform as string;
57+
}
58+
59+
private get target(): string {
60+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
61+
return this.flagValues.target as string;
62+
}
63+
private get appBundlePath(): string {
64+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
65+
return this.flagValues.appbundlepath as string;
66+
}
67+
68+
protected static getOutputSchema(): z.ZodTypeAny {
69+
return DeviceOperationResultSchema;
70+
}
71+
72+
public async run(): Promise<DeviceOperationResultType | void> {
73+
this.logger.info(`app install command invoked for ${this.platform}`);
74+
75+
const successMessage = messages.getMessage('app.install.successStatus', [this.appBundlePath, this.target]);
76+
77+
try {
78+
// if not in JSON mode, execute the requirements and start the CLI action
79+
if (!this.jsonEnabled()) {
80+
await RequirementProcessor.execute(this.commandRequirements);
81+
this.logger.info('Setup requirements met, continuing with app install');
82+
CommonUtils.startCliAction(
83+
messages.getMessage('app.install.action'),
84+
messages.getMessage('app.install.status', [this.appBundlePath, this.target])
85+
);
86+
}
87+
88+
// execute the app install command
89+
await this.executeAppInstall();
90+
91+
if (this.jsonEnabled()) {
92+
// In JSON mode, get the device and return it in the format of the DeviceSchema
93+
const deviceManager = CommandLineUtils.platformFlagIsAndroid(this.platform)
94+
? new AndroidDeviceManager()
95+
: new AppleDeviceManager();
96+
const device = await deviceManager.getDevice(this.target);
97+
98+
if (!device) {
99+
throw new Error(messages.getMessage('error.target.doesNotExist', [this.target]));
100+
}
101+
102+
const deviceInfo = DeviceSchema.parse(device);
103+
return {
104+
device: deviceInfo,
105+
success: true,
106+
message: successMessage
107+
};
108+
} else {
109+
// if cli mode, stop the CLI action
110+
CommonUtils.stopCliAction(successMessage);
111+
}
112+
} catch (error) {
113+
if (!this.jsonEnabled()) {
114+
// if cli mode, stop the CLI action
115+
CommonUtils.stopCliAction(messages.getMessage('app.install.failureStatus'));
116+
}
117+
this.logger.warn(`App Install failed for ${this.platform} - ${(error as Error).message}`);
118+
throw error;
119+
}
120+
}
121+
122+
protected populateCommandRequirements(): void {
123+
const requirements: CommandRequirements = {};
124+
125+
// check environment is setup correctly for the platform
126+
requirements.setup = CommandLineUtils.platformFlagIsAndroid(this.platform)
127+
? new AndroidEnvironmentRequirements(this.logger)
128+
: new IOSEnvironmentRequirements(this.logger);
129+
130+
this.commandRequirements = requirements;
131+
}
132+
133+
private async executeAppInstall(): Promise<void> {
134+
const deviceManager = CommandLineUtils.platformFlagIsAndroid(this.platform)
135+
? new AndroidDeviceManager()
136+
: new AppleDeviceManager();
137+
const device = await deviceManager.getDevice(this.target);
138+
139+
if (!device) {
140+
return Promise.reject(messages.getMessage('error.target.doesNotExist', [this.target]));
141+
}
142+
143+
await device.boot(true);
144+
await device.installApp(this.appBundlePath);
145+
}
146+
}

0 commit comments

Comments
 (0)