diff --git a/README.md b/README.md index 25ca5a2..e7214f6 100644 --- a/README.md +++ b/README.md @@ -201,7 +201,7 @@ 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.0/src/commands/lightning/dev/app.ts)_ +_See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.1-alpha.0/src/commands/lightning/dev/app.ts)_ ## `sf lightning dev component` @@ -209,13 +209,14 @@ _See code: [src/commands/lightning/dev/app.ts](https://github.com/salesforcecli/ ``` USAGE - $ sf lightning dev component -o [--json] [--flags-dir ] [-n ] [-c] + $ sf lightning dev component -o [--json] [--flags-dir ] [-n ] [--api-version ] [-c] FLAGS - -c, --client-select Launch component preview without selecting a component - -n, --name= Name of a component to preview. - -o, --target-org= (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= Name of a component to preview. + -o, --target-org= (required) Username or alias of the target org. Not required if the `target-org` + configuration variable is already set. + --api-version= Override the api version used for api requests made by this command GLOBAL FLAGS --flags-dir= Import flag values from a directory. @@ -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.0/src/commands/lightning/dev/component.ts)_ +_See code: [src/commands/lightning/dev/component.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.1-alpha.0/src/commands/lightning/dev/component.ts)_ ## `sf lightning dev site` @@ -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.0/src/commands/lightning/dev/site.ts)_ +_See code: [src/commands/lightning/dev/site.ts](https://github.com/salesforcecli/plugin-lightning-dev/blob/4.4.1-alpha.0/src/commands/lightning/dev/site.ts)_ diff --git a/command-snapshot.json b/command-snapshot.json index aa16dcf..26cc62d 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -12,7 +12,7 @@ "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" }, { @@ -20,7 +20,7 @@ "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" } ] diff --git a/package.json b/package.json index 8ea301f..551d69f 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@salesforce/plugin-lightning-dev", "description": "Lightning development tools for LEX, Mobile, and Experience Sites", - "version": "4.4.0", + "version": "4.4.1-alpha.0", "author": "Salesforce", "bugs": "https://github.com/forcedotcom/cli/issues", "dependencies": { diff --git a/src/commands/lightning/dev/component.ts b/src/commands/lightning/dev/component.ts index 97a1fab..0431528 100644 --- a/src/commands/lightning/dev/component.ts +++ b/src/commands/lightning/dev/component.ts @@ -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 { +export type ComponentPreviewResult = { + instanceUrl: string; + ldpServerUrl: string; + ldpServerId: string; + componentName: string; + previewUrl: string; +}; + +export default class LightningDevComponent extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); @@ -29,6 +37,7 @@ export default class LightningDevComponent extends SfCommand { char: 'n', requiredOrDefaulted: false, }), + 'api-version': Flags.orgApiVersion(), 'client-select': Flags.boolean({ summary: messages.getMessage('flags.client-select.summary'), char: 'c', @@ -37,7 +46,7 @@ export default class LightningDevComponent extends SfCommand { 'target-org': Flags.requiredOrg(), }; - public async run(): Promise { + public async run(): Promise { const { flags } = await this.parse(LightningDevComponent); const logger = await Logger.child(this.ctor.name); const project = await SfProject.resolve(); @@ -54,6 +63,7 @@ export default class LightningDevComponent extends SfCommand { 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); @@ -65,41 +75,41 @@ export default class LightningDevComponent extends SfCommand { const 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( @@ -119,7 +129,9 @@ export default class LightningDevComponent extends SfCommand { } } - 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( @@ -129,7 +141,31 @@ export default class LightningDevComponent extends SfCommand { 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; } } diff --git a/src/shared/previewUtils.ts b/src/shared/previewUtils.ts index 64ff30a..ff28e26 100644 --- a/src/shared/previewUtils.ts +++ b/src/shared/previewUtils.ts @@ -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. *