Skip to content
Open
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
19 changes: 19 additions & 0 deletions command-snapshot.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,25 @@
"flags": ["api-version", "flags-dir", "internal", "json", "loglevel", "name", "output-dir", "template"],
"plugin": "@salesforce/plugin-templates"
},
{
"alias": [],
"command": "template:generate:microfrontend",
"flagAliases": ["apiversion", "outputdir"],
"flagChars": ["d", "i", "n", "s"],
"flags": [
"api-version",
"flags-dir",
"internal",
"json",
"loglevel",
"name",
"output-dir",
"sandbox",
"shell-title",
"src"
],
"plugin": "@salesforce/plugin-templates"
},
{
"alias": ["force:project:create", "project:generate"],
"command": "template:generate:project",
Expand Down
51 changes: 51 additions & 0 deletions messages/microfrontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# examples

- Generate an MFE wrapper LWC in the current directory:

<%= config.bin %> <%= command.id %> --name MyMfeWrapper --src https://app.example.com --sandbox allow-forms --shell-title "Expense Report Widget"

- Generate an MFE wrapper LWC in the "force-app/main/default/lwc" directory with multiple sandbox tokens:

<%= config.bin %> <%= command.id %> --name MyMfeWrapper --src https://app.example.com --sandbox allow-forms --sandbox allow-scripts --shell-title "Expense Report Widget" --output-dir force-app/main/default/lwc

# summary

Generate a Lightning Web Component that wraps the lightning-mfe-shell base component.

# description

Generates a Lightning Web Component bundle that consumes the first-party <lightning-mfe-shell> component, pre-wired with the three required attributes: the widget URL (src), iframe sandbox tokens, and an accessible iframe title (shell-title).

The generated bundle contains four files (.html, .js, .js-meta.xml, .css) in a directory named with the camelCased component name. The bundle must live under a parent folder named "lwc".

# flags.name.summary

PascalCase name of the generated component.

# flags.name.description

The component name is also used (camelCased) as the LWC folder name and file stem. Must contain only alphanumeric characters and start with a letter.

# flags.src.summary

Absolute https URL the iframe will load.

# flags.src.description

The URL is bound to the <lightning-mfe-shell> "src" attribute as a reactive property in the generated LWC. Must use https; plain http is only allowed for localhost or 127.0.0.1 (for local development).

# flags.sandbox.summary

Iframe sandbox token (specify the flag multiple times to set more than one token).

# flags.sandbox.description

Each token is written into the space-separated "sandbox" attribute on <lightning-mfe-shell>. Only W3C-defined sandbox tokens are accepted.

# flags.shell-title.summary

Accessible title for the embedded iframe.

# flags.shell-title.description

Written to the "shell-title" attribute on <lightning-mfe-shell> and used as the iframe's accessible name (announced by screen readers). Must be a non-empty string.
89 changes: 89 additions & 0 deletions src/commands/template/generate/microfrontend.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) 2026, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/

import { Flags, loglevel, orgApiVersionFlagWithDeprecations, SfCommand, Ux } from '@salesforce/sf-plugins-core';
import { CreateOutput, MicrofrontendOptions, TemplateType } from '@salesforce/templates';
import { Messages } from '@salesforce/core';
import { getCustomTemplates, runGenerator } from '../../../utils/templateCommand.js';
import { internalFlag, outputDirFlagLightning } from '../../../utils/flags.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('@salesforce/plugin-templates', 'microfrontend');

// Keep in-sync with VALID_SANDBOX_TOKENS in salesforcedx-templates/src/generators/microfrontendGenerator.ts
const SANDBOX_TOKENS = [
'allow-forms',
'allow-modals',
'allow-orientation-lock',
'allow-pointer-lock',
'allow-popups',
'allow-popups-to-escape-sandbox',
'allow-presentation',
'allow-same-origin',
'allow-scripts',
'allow-storage-access-by-user-activation',
'allow-top-navigation',
'allow-top-navigation-by-user-activation',
] as const;

export default class Microfrontend extends SfCommand<CreateOutput> {
public static readonly summary = messages.getMessage('summary');
public static readonly description = messages.getMessage('description');
public static readonly examples = messages.getMessages('examples');
public static readonly hidden = true;

public static readonly flags = {
name: Flags.string({
char: 'n',
summary: messages.getMessage('flags.name.summary'),
description: messages.getMessage('flags.name.description'),
required: true,
}),
src: Flags.string({
char: 's',
summary: messages.getMessage('flags.src.summary'),
description: messages.getMessage('flags.src.description'),
required: true,
}),
sandbox: Flags.option({
summary: messages.getMessage('flags.sandbox.summary'),
description: messages.getMessage('flags.sandbox.description'),
options: SANDBOX_TOKENS,
multiple: true,
required: true,
})(),
'shell-title': Flags.string({
summary: messages.getMessage('flags.shell-title.summary'),
description: messages.getMessage('flags.shell-title.description'),
}),
'output-dir': outputDirFlagLightning,
'api-version': orgApiVersionFlagWithDeprecations,
internal: internalFlag,
loglevel,
};

public async run(): Promise<CreateOutput> {
const { flags } = await this.parse(Microfrontend);

const flagsAsOptions: MicrofrontendOptions = {
componentname: flags.name,
src: flags.src,
sandbox: flags.sandbox.join(' '),
shellTitle: flags['shell-title'] ?? flags.name,
outputdir: flags['output-dir'],
apiversion: flags['api-version'],
internal: flags.internal,
};

return runGenerator({
templateType: TemplateType.Microfrontend,
opts: flagsAsOptions,
ux: new Ux({ jsonEnabled: this.jsonEnabled() }),
templates: getCustomTemplates(this.configAggregator),
});
}
}