Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions messages/prompts.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ Unable to enumerate a list of available devices.
# component.select

Which Lightning Web Component would you like to preview (Use arrow keys)

# component.enable-local-dev

Local dev isn't enabled for this org. Enable it?
4 changes: 4 additions & 0 deletions messages/shared.utils.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ The SSL certificate data to be used by the local dev server for secure connectio

You must provide valid SSL certificate data

# localdev.enabled

Local dev has been enabled for this org.

# error.localdev.not.enabled

Local Dev is not enabled for your org. See https://developer.salesforce.com/docs/platform/lwc/guide/get-started-test-components.html for more information on enabling and using Local Dev.
Expand Down
6 changes: 6 additions & 0 deletions src/commands/lightning/dev/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { Flags, SfCommand } from '@salesforce/sf-plugins-core';
import { startLWCServer } from '../../../lwc-dev-server/index.js';
import { PreviewUtils } from '../../../shared/previewUtils.js';
import { PromptUtils } from '../../../shared/promptUtils.js';
import { MetaUtils } from '../../../shared/metaUtils.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.app');
Expand Down Expand Up @@ -86,6 +87,11 @@ export default class LightningDevApp extends SfCommand<void> {
}

logger.debug('Initalizing preview connection and configuring local web server identity');

if (await MetaUtils.handleLocalDevEnablement(targetOrg.getConnection(undefined))) {
this.log(sharedMessages.getMessage('localdev.enabled'));
}

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

const platform = flags['device-type'] ?? (await PromptUtils.promptUserToSelectPlatform());
Expand Down
10 changes: 2 additions & 8 deletions src/commands/lightning/dev/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,8 @@ export default class LightningDevComponent extends SfCommand<ComponentPreviewRes
const targetOrg = flags['target-org'];
const apiVersion = flags['api-version'];

// Auto enable local dev
if (process.env.AUTO_ENABLE_LOCAL_DEV === 'true') {
try {
await MetaUtils.ensureLightningPreviewEnabled(targetOrg.getConnection(undefined));
await MetaUtils.ensureFirstPartyCookiesNotRequired(targetOrg.getConnection(undefined));
} catch (error) {
this.log('Error autoenabling local dev', error);
}
if (await MetaUtils.handleLocalDevEnablement(targetOrg.getConnection(undefined))) {
this.log(sharedMessages.getMessage('localdev.enabled'));
}

const { ldpServerId, ldpServerToken } = await PreviewUtils.initializePreviewConnection(targetOrg);
Expand Down
6 changes: 3 additions & 3 deletions src/commands/lightning/dev/site.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { PromptUtils } from '../../../shared/promptUtils.js';
import { ExperienceSite } from '../../../shared/experience/expSite.js';
import { PreviewUtils } from '../../../shared/previewUtils.js';
import { startLWCServer } from '../../../lwc-dev-server/index.js';
import { MetaUtils } from '../../../shared/metaUtils.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'lightning.dev.site');
Expand Down Expand Up @@ -67,9 +68,8 @@ export default class LightningDevSite extends SfCommand<void> {

const connection = org.getConnection(undefined);

const localDevEnabled = await OrgUtils.isLocalDevEnabled(connection);
if (!localDevEnabled) {
throw new Error(sharedMessages.getMessage('error.localdev.not.enabled'));
if (await MetaUtils.handleLocalDevEnablement(connection)) {
this.log(sharedMessages.getMessage('localdev.enabled'));
}

OrgUtils.ensureMatchingAPIVersion(connection);
Expand Down
56 changes: 36 additions & 20 deletions src/shared/metaUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
* limitations under the License.
*/

import { Connection, Logger } from '@salesforce/core';
import { Connection, Logger, Messages } from '@salesforce/core';
import { PromptUtils } from './promptUtils.js';

type LightningExperienceSettingsMetadata = {
[key: string]: unknown;
Expand All @@ -34,6 +35,8 @@ type MetadataUpdateResult = {
errors?: Array<{ message: string }>;
};

const sharedMessages = Messages.loadMessages('@salesforce/plugin-lightning-dev', 'shared.utils');

/**
* Utility class for managing Salesforce metadata settings related to Lightning Development.
*/
Expand Down Expand Up @@ -199,25 +202,6 @@ export class MetaUtils {
this.logger.debug('Successfully updated first-party cookie requirement');
}

/**
* Ensures Lightning Preview is enabled for the org. If it's not enabled, this method will enable it.
*
* @param connection the connection to the org
* @returns boolean indicating whether Lightning Preview was already enabled (true) or had to be enabled (false)
*/
public static async ensureLightningPreviewEnabled(connection: Connection): Promise<boolean> {
const isEnabled = await this.isLightningPreviewEnabled(connection);

if (!isEnabled) {
this.logger.info('Lightning Preview is not enabled. Enabling it now...');
await this.setLightningPreviewEnabled(connection, true);
return false;
}

this.logger.debug('Lightning Preview is already enabled');
return true;
}

/**
* Ensures first-party cookies are not required for the org. If they are required, this method will disable the requirement.
*
Expand All @@ -236,4 +220,36 @@ export class MetaUtils {
this.logger.debug('First-party cookies are not required');
return true;
}

/**
* Enables local dev if required and permitted. If executed via VSCode command
* the user's response is already assigned to AUTO_ENABLE_LOCAL_DEV and it will be used.
* If executed via command line, this method will prompt the user.
*
* @param connection the connection to the org
* @returns true if enabled
* @throws local dev not enabled error if not enabled
*/
public static async handleLocalDevEnablement(connection: Connection): Promise<boolean | undefined> {
const isLightningPreviewEnabled = await this.isLightningPreviewEnabled(connection);

if (!isLightningPreviewEnabled) {
const autoEnableLocalDev = process.env.AUTO_ENABLE_LOCAL_DEV;

// If executed via VSCode command, autoEnableLocalDev will contain the users choice, provided via UI.
// Else, prompt the user on the command line.
const enableLocalDev =
autoEnableLocalDev !== undefined
? autoEnableLocalDev === 'true'
: await PromptUtils.promptUserToEnableLocalDev();

if (enableLocalDev) {
await this.setLightningPreviewEnabled(connection, true);
await this.ensureFirstPartyCookiesNotRequired(connection);
Copy link
Collaborator

@nrkruk nrkruk Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first party cookie setting is only needed to render live preview in VSCode. I'm not sure if we should be explicit about what we are doing here as this won't get turned off when you disable Local Dev.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nrkruk by explicit you mean only set it if the AUTO_ENABLE_LOCAL_DEV is set to true (aka VSCode initiated)?

Copy link
Collaborator

@nrkruk nrkruk Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats what I was doing today (which is hacky), but I want us to think about what the right solution is here.

At a minimum, we need to let the user know that we are changing this setting on their behalf (in addition to enabling local dev). And I don't think we should be changing the cookie setting if the user is just running the command directly from the CLI.

Might be possible to just put the logic for this in the VSCode extension directly (rather than the VSCode telling the CLI to change the cookie setting), but I haven't looked at it.

return true;
} else {
throw new Error(sharedMessages.getMessage('error.localdev.not.enabled'));
}
}
}
}
5 changes: 0 additions & 5 deletions src/shared/previewUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,6 @@ export class PreviewUtils {
return Promise.reject(new Error(sharedMessages.getMessage('error.username')));
}

const localDevEnabled = await OrgUtils.isLocalDevEnabled(connection);
if (!localDevEnabled) {
return Promise.reject(new Error(sharedMessages.getMessage('error.localdev.not.enabled')));
}

OrgUtils.ensureMatchingAPIVersion(connection);

const appServerIdentity = await PreviewUtils.getOrCreateAppServerIdentity(connection);
Expand Down
7 changes: 7 additions & 0 deletions src/shared/promptUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ export class PromptUtils {
return response;
}

public static async promptUserToEnableLocalDev(): Promise<boolean> {
return confirm({
message: messages.getMessage('component.enable-local-dev'),
default: true,
});
}

// returns the shorthand version of a Version object (eg. 17.0.0 => 17, 17.4.0 => 17.4, 17.4.1 => 17.4.1)
private static getShortVersion(version: Version | string): string {
// TODO: consider making this function part of the Version class in @lwc-dev-mobile-core
Expand Down
Loading