Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
24b1a10
chore: update task label and arguments in tasks.json for CLI plugin b…
nrkruk Jul 16, 2025
33fff08
feat: add comprehensive cursor rules documentation for Salesforce CLI…
nrkruk Jul 16, 2025
5111e3b
feat: enhance component preview functionality by generating and loggi…
nrkruk Jul 16, 2025
b2a05fd
feat: enhance LightningDevComponent to return json
nrkruk Jul 17, 2025
03e044b
feat: update command-snapshot.json to include new flags for Lightning…
nrkruk Jul 17, 2025
4fdf6d0
Merge branch 'main' into nkruk/updatePreviewCommand
nrkruk Jul 18, 2025
5a5bef1
chore: merge branch 'main' into nkruk/updatePreviewCommand
nrkruk Jul 18, 2025
6a5cd6a
chore(release): 4.4.1-alpha.0 [skip ci]
svc-cli-bot Jul 18, 2025
2b081f2
chore: upgrade LWC dependencies to version 13.0.31
nrkruk Jul 21, 2025
b9d163d
Merge branch 'main' into nkruk/updatePreviewCommand
nrkruk Jul 22, 2025
4c69ff5
chore(release): 4.4.2-alpha.0 [skip ci]
svc-cli-bot Aug 4, 2025
e0b24ad
Merge branch 'main' into nkruk/updatePreviewCommand
nrkruk Aug 21, 2025
e7469b3
fix: temp package json changes
nrkruk Aug 21, 2025
c0a484f
fix: lock file
nrkruk Aug 25, 2025
4a98801
chore(release): 4.4.13-alpha.0 [skip ci]
svc-cli-bot Aug 25, 2025
5c44b5d
feat: update local dev server URL handling for CODE_BUILDER_URI envir…
nrkruk Sep 9, 2025
bac4800
refactor: change variable declaration for local dev server URL in Lig…
nrkruk Sep 9, 2025
bb7ba65
chore(release): 4.4.13-codebuilder.0 [skip ci]
svc-cli-bot Sep 9, 2025
fd83961
chore(release): 4.4.13-codebuilder.1 [skip ci]
svc-cli-bot Sep 9, 2025
3aae43b
feat: enhance local dev server URL handling for Code Builder environment
nrkruk Sep 11, 2025
fbeb872
chore(release): 4.4.13-codebuilder.2 [skip ci]
svc-cli-bot Sep 11, 2025
ec029c1
fix: code builder trusted origins
nrkruk Oct 5, 2025
7411dfa
chore(release): 4.4.13-codebuilder.3 [skip ci]
svc-cli-bot Oct 5, 2025
49a2ca9
feat: auto close lwc-dev-server window in code builder
nrkruk Oct 5, 2025
8c591be
chore(release): 4.4.13-codebuilder.4 [skip ci]
svc-cli-bot Oct 5, 2025
6f27fc7
fix: code builder domains
nrkruk Oct 5, 2025
4e1ce16
chore(release): 4.4.13-codebuilder.5 [skip ci]
svc-cli-bot Oct 5, 2025
21c5084
fix: new component creation
nrkruk Oct 9, 2025
a619436
chore(release): 4.4.13-codebuilder.6 [skip ci]
svc-cli-bot Oct 9, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -201,21 +201,22 @@ EXAMPLES
$ sf lightning dev app --target-org myOrg --device-type ios --device-id "iPhone 15 Pro Max"
```

_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.12/src/commands/lightning/dev/app.ts)_
_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.13-codebuilder.6/src/commands/lightning/dev/app.ts)_

## `sf lightning dev component`

[Beta] Preview LWC components in isolation.

```
USAGE
$ sf lightning dev component -o <value> [--json] [--flags-dir <value>] [-n <value>] [-c]
$ sf lightning dev component -o <value> [--json] [--flags-dir <value>] [-n <value>] [--api-version <value>] [-c]

FLAGS
-c, --client-select Launch component preview without selecting a component
-n, --name=<value> Name of a component to preview.
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
-c, --client-select Launch component preview without selecting a component
-n, --name=<value> Name of a component to preview.
-o, --target-org=<value> (required) Username or alias of the target org. Not required if the `target-org`
configuration variable is already set.
--api-version=<value> Override the api version used for api requests made by this command

GLOBAL FLAGS
--flags-dir=<value> Import flag values from a directory.
Expand Down Expand Up @@ -248,7 +249,7 @@ EXAMPLES
$ sf lightning dev component --name myComponent
```

_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.12/src/commands/lightning/dev/component.ts)_
_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.13-codebuilder.6/src/commands/lightning/dev/component.ts)_

## `sf lightning dev site`

Expand Down Expand Up @@ -304,6 +305,6 @@ EXAMPLES
$ sf lightning dev site --name "Partner Central" --target-org myOrg --get-latest
```

_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.12/src/commands/lightning/dev/site.ts)_
_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.13-codebuilder.6/src/commands/lightning/dev/site.ts)_

<!-- commandsstop -->
4 changes: 2 additions & 2 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@
"command": "lightning:dev:component",
"flagAliases": [],
"flagChars": ["c", "n", "o"],
"flags": ["client-select", "flags-dir", "json", "name", "target-org"],
"flags": ["api-version", "client-select", "flags-dir", "json", "name", "target-org"],
"plugin": "@salesforce/plugin-lightning-dev"
},
{
"alias": [],
"command": "lightning:dev:site",
"flagAliases": [],
"flagChars": ["l", "n", "o"],
"flags": ["flags-dir", "get-latest", "guest", "name", "target-org", "ssr"],
"flags": ["flags-dir", "get-latest", "guest", "name", "ssr", "target-org"],
"plugin": "@salesforce/plugin-lightning-dev"
}
]
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
{
"name": "@salesforce/plugin-lightning-dev",
"description": "Lightning development tools for LEX, Mobile, and Experience Sites",
"version": "4.4.12",
"version": "4.4.13-codebuilder.6",
"author": "Salesforce",
"bugs": "https://github.com/forcedotcom/cli/issues",
"dependencies": {
"@inquirer/prompts": "^5.3.8",
"@inquirer/select": "^2.4.7",
"@lwc/lwc-dev-server": "~13.2.1",
"@lwc/sfdc-lwc-compiler": "~13.0.31",
"@lwc/lwc-dev-server": "~13.1.5-alpha.1+9de6b34",
"@lwc/sfdc-lwc-compiler": "~13.1.5-alpha.1+9de6b34",
"@lwrjs/api": "0.18.3",
"@oclif/core": "^4.5.0",
"@salesforce/core": "^8.19.1",
Expand Down Expand Up @@ -237,8 +237,8 @@
"The 'versionToTagMappings' section defines the mapping between released tags for our CLI plugin and the org version that each tag supports."
],
"target": {
"versionNumber": "66.0",
"matchingDevServerVersion": "~13.0.27"
"versionNumber": "65.0",
"matchingDevServerVersion": "~13.1.5-alpha.1+9de6b34"
},
"versionToTagMappings": [
{
Expand All @@ -254,7 +254,7 @@
"tagName": "prerelease"
},
{
"versionNumber": "66.0",
"versionNumber": "65.0",
"tagName": "next"
}
]
Expand Down
126 changes: 87 additions & 39 deletions src/commands/lightning/dev/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.component');
const sharedMessages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'shared.utils');

export default class LightningDevComponent extends SfCommand<void> {
export type ComponentPreviewResult = {
instanceUrl: string;
ldpServerUrl: string;
ldpServerId: string;
componentName: string;
previewUrl: string;
};

export default class LightningDevComponent extends SfCommand<ComponentPreviewResult> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
Expand All @@ -29,6 +37,7 @@ export default class LightningDevComponent extends SfCommand<void> {
char: 'n',
requiredOrDefaulted: false,
}),
'api-version': Flags.orgApiVersion(),
'client-select': Flags.boolean({
summary: messages.getMessage('flags.client-select.summary'),
char: 'c',
Expand All @@ -37,7 +46,7 @@ export default class LightningDevComponent extends SfCommand<void> {
'target-org': Flags.requiredOrg(),
};

public async run(): Promise<void> {
public async run(): Promise<ComponentPreviewResult> {
const { flags } = await this.parse(LightningDevComponent);
const logger = await Logger.child(this.ctor.name);
const project = await SfProject.resolve();
Expand All @@ -54,6 +63,7 @@ export default class LightningDevComponent extends SfCommand<void> {
let componentName = flags['name'];
const clientSelect = flags['client-select'];
const targetOrg = flags['target-org'];
const apiVersion = flags['api-version'];

const { ldpServerId, ldpServerToken } = await PreviewUtils.initializePreviewConnection(targetOrg);

Expand All @@ -62,44 +72,56 @@ export default class LightningDevComponent extends SfCommand<void> {
logger.debug(`Next available ports are http=${serverPorts.httpPort} , https=${serverPorts.httpsPort}`);

logger.debug('Determining Local Dev Server url');
const ldpServerUrl = PreviewUtils.generateWebSocketUrlForLocalDevServer(Platform.desktop, serverPorts, logger);
let ldpServerUrl;

// In Code Builder, we cannot go to localhost - we need to use a proxy URI to get to the ldpServer
if (process.env.SF_CONTAINER_MODE && process.env.VSCODE_PROXY_URI) {
logger.debug('In Code Builder Mode - using proxy URI');
ldpServerUrl = process.env.VSCODE_PROXY_URI.replace('https://', 'ws://').replace(
'{{port}}',
`${serverPorts.httpPort}`
);
} else {
// Default behavior
ldpServerUrl = PreviewUtils.generateWebSocketUrlForLocalDevServer(Platform.desktop, serverPorts, logger);
}
logger.debug(`Local Dev Server url is ${ldpServerUrl}`);

const namespacePaths = await ComponentUtils.getNamespacePaths(project);
const componentPaths = await ComponentUtils.getAllComponentPaths(namespacePaths);
if (!componentPaths) {
throw new Error(messages.getMessage('error.directory'));
}
if (!clientSelect) {
const namespacePaths = await ComponentUtils.getNamespacePaths(project);
const componentPaths = await ComponentUtils.getAllComponentPaths(namespacePaths);
if (!componentPaths) {
throw new Error(messages.getMessage('error.directory'));
}

const components = (
await Promise.all(
componentPaths.map(async (componentPath) => {
let xml;

try {
xml = await ComponentUtils.getComponentMetadata(componentPath);
} catch (err) {
this.warn(messages.getMessage('error.component-metadata', [componentPath]));
}

// components must have meta xml to be previewed
if (!xml) {
return undefined;
}

const name = path.basename(componentPath);
const label = ComponentUtils.componentNameToTitleCase(name);

return {
name,
label: xml.LightningComponentBundle.masterLabel ?? label,
description: xml.LightningComponentBundle.description ?? '',
};
})
)
).filter((component) => !!component);
const components = (
await Promise.all(
componentPaths.map(async (componentPath) => {
let xml;

try {
xml = await ComponentUtils.getComponentMetadata(componentPath);
} catch (err) {
this.warn(messages.getMessage('error.component-metadata', [componentPath]));
}

// components must have meta xml to be previewed
if (!xml) {
return undefined;
}

const name = path.basename(componentPath);
const label = ComponentUtils.componentNameToTitleCase(name);

return {
name,
label: xml.LightningComponentBundle.masterLabel ?? label,
description: xml.LightningComponentBundle.description ?? '',
};
})
)
).filter((component) => !!component);

if (!clientSelect) {
if (componentName) {
// validate that the component exists before launching the server
const match = components.find(
Expand All @@ -119,7 +141,9 @@ export default class LightningDevComponent extends SfCommand<void> {
}
}

await startLWCServer(logger, sfdxProjectRootPath, ldpServerToken, Platform.desktop, serverPorts);
if (process.env.LAUNCH_SERVER !== 'false') {
await startLWCServer(logger, sfdxProjectRootPath, ldpServerToken, Platform.desktop, serverPorts);
}

const targetOrgArg = PreviewUtils.getTargetOrgFromArguments(this.argv);
const launchArguments = PreviewUtils.generateComponentPreviewLaunchArguments(
Expand All @@ -129,7 +153,31 @@ export default class LightningDevComponent extends SfCommand<void> {
targetOrgArg
);

// Open the browser and navigate to the right page
await this.config.runCommand('org:open', launchArguments);
// Construct and log the full URL that will be opened
const connection = targetOrg.getConnection(apiVersion);

const previewUrl = PreviewUtils.generateComponentPreviewUrl(
connection.instanceUrl,
ldpServerUrl,
ldpServerId,
componentName,
false
);

// Prepare the result for JSON output
const result: ComponentPreviewResult = {
instanceUrl: connection.instanceUrl,
ldpServerUrl,
ldpServerId,
componentName: componentName ?? '',
previewUrl,
};

// Open the browser and navigate to the right page (unless OPEN_BROWSER is set to true)
if (process.env.OPEN_BROWSER !== 'false') {
await this.config.runCommand('org:open', launchArguments);
}

return result;
}
}
28 changes: 28 additions & 0 deletions src/shared/previewUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,34 @@ export class PreviewUtils {
return launchArguments;
}

/**
* Generates the full URL for a component preview.
*
* @param instanceUrl The URL of the Salesforce instance
* @param ldpServerUrl The URL for the local dev server
* @param ldpServerId Record ID for the identity token
* @param componentName The name of the component to preview
* @param encodePath Whether to encode the path
* @returns The full URL for the component preview
*/
public static generateComponentPreviewUrl(
instanceUrl: string,
ldpServerUrl: string,
ldpServerId: string,
componentName?: string,
encodePath = false
): string {
let url = `${instanceUrl}/lwr/application/e/devpreview/ai/${
encodePath ? encodeURIComponent('localdev%2Fpreview') : 'localdev%2Fpreview'
}?ldpServerUrl=${ldpServerUrl}&ldpServerId=${ldpServerId}`;
if (componentName) {
// TODO: support other namespaces
url += `&specifier=c/${componentName}`;
}

return url;
}

/**
* Generates the proper set of arguments to be used for launching a mobile app with custom launch arguments.
*
Expand Down
Loading