Skip to content

Commit 78bd2d7

Browse files
authored
Merge pull request #833 from inba-ganapathy/feat/flexipage-generator
W-21017394 feat: add sf flexipage generate command
2 parents 6aac61c + 584bc37 commit 78bd2d7

7 files changed

Lines changed: 503 additions & 2 deletions

File tree

command-snapshot.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,29 @@
3131
"flags": ["admin-email", "flags-dir", "json", "name", "output-dir", "target-org", "template", "url-path-prefix"],
3232
"plugin": "@salesforce/plugin-templates"
3333
},
34+
{
35+
"alias": [],
36+
"command": "template:generate:flexipage",
37+
"flagAliases": ["apiversion", "entity", "entity-name", "flexipagename", "masterlabel", "outputdir"],
38+
"flagChars": ["d", "i", "n", "s", "t"],
39+
"flags": [
40+
"api-version",
41+
"description",
42+
"detail-fields",
43+
"flags-dir",
44+
"internal",
45+
"json",
46+
"label",
47+
"loglevel",
48+
"name",
49+
"output-dir",
50+
"primary-field",
51+
"secondary-fields",
52+
"sobject",
53+
"template"
54+
],
55+
"plugin": "@salesforce/plugin-templates"
56+
},
3457
{
3558
"alias": ["force:lightning:app:create", "lightning:generate:app"],
3659
"command": "template:generate:lightning:app",

messages/digitalExperienceSite.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Creates an Experience Cloud site with the specified template, name, and URL path
1212

1313
<%= config.bin %> <%= command.id %> --template BuildYourOwnLWR --name mysite --url-path-prefix mysite
1414

15-
- Generate an Experience Cloud site like the last example, but generate the files into the specified output directory:
15+
- Generate an Experience Cloud site like the last example, but generate the files into the specified output directory:
1616

1717
<%= config.bin %> <%= command.id %> --template BuildYourOwnLWR --name mysite --url-path-prefix mysite --output-dir force-app/main/default
1818

@@ -28,7 +28,7 @@ Template to use when generating the site.
2828

2929
Supported templates:
3030

31-
- BuildYourOwnLWR - Creates blazing-fast digital experiences, such as websites, microsites, and portals, using the Lightning Web Components programming model. Powered by Lightning Web Runtime (LWR), this customizable template delivers unparalleled site performance. For additional details, see this Salesforce Help topic: https://help.salesforce.com/s/articleView?id=experience.rss_build_your_own_lwr.htm.
31+
- BuildYourOwnLWR - Creates blazing-fast digital experiences, such as websites, microsites, and portals, using the Lightning Web Components programming model. Powered by Lightning Web Runtime (LWR), this customizable template delivers unparalleled site performance. For additional details, see this Salesforce Help topic: https://help.salesforce.com/s/articleView?id=experience.rss_build_your_own_lwr.htm.
3232

3333
# flags.url-path-prefix.summary
3434

messages/flexipage.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# examples
2+
3+
- Generate a RecordPage FlexiPage for the Account object in the current directory:
4+
5+
<%= config.bin %> <%= command.id %> --name Account_Record_Page --template RecordPage --sobject Account
6+
7+
- Generate an AppPage FlexiPage in the "force-app/main/default/flexipages" directory:
8+
9+
<%= config.bin %> <%= command.id %> --name Sales_Dashboard --template AppPage --output-dir force-app/main/default/flexipages
10+
11+
- Generate a HomePage FlexiPage with a custom label:
12+
13+
<%= config.bin %> <%= command.id %> --name Custom_Home --template HomePage --label "Sales Home Page"
14+
15+
- Generate a RecordPage with dynamic highlights and detail fields:
16+
17+
<%= config.bin %> <%= command.id %> --name Property_Page --template RecordPage --sobject Rental_Property__c --primary-field Name --secondary-fields Property_Address__c,City__c --detail-fields Name,Property_Address__c,City__c,Monthly_Rent__c,Bedrooms__c
18+
19+
# summary
20+
21+
Generate a FlexiPage, also known as a Lightning page.
22+
23+
# description
24+
25+
FlexiPages are the metadata types associated with a Lightning page. A Lightning page represents a customizable screen made up of regions containing Lightning components.
26+
27+
You can use this command to generate these types of FlexiPages; specify the type with the --template flag:
28+
29+
- AppPage: A Lightning page used as the home page for a custom app or a standalone application page.
30+
- HomePage: A Lightning page used to override the Home page in Lightning Experience.
31+
- RecordPage: A Lightning page used to override an object record page in Lightning Experience. Requires that you specify the object name with the --sobject flag.
32+
33+
# flags.name.summary
34+
35+
Name of the FlexiPage.
36+
37+
# flags.name.description
38+
39+
The name can contain only alphanumeric characters, must start with a letter, and can't end with an underscore or contain two consecutive underscores.
40+
41+
# flags.template.summary
42+
43+
Template type for the FlexiPage.
44+
45+
# flags.label.summary
46+
47+
Label of this FlexiPage; if not specified, uses the FlexiPage name as the label.
48+
49+
# flags.description.summary
50+
51+
Description for the FlexiPage, which provides context about its purpose.
52+
53+
# flags.sobject.summary
54+
55+
API name of the Salesforce object; required when creating a RecordPage.
56+
57+
# flags.sobject.description
58+
59+
For RecordPage FlexiPages, you must specify the associated object API name, such as 'Account', 'Opportunity', or 'Custom_Object__c'. This sets the `sobjectType` field in the FlexiPage metadata.
60+
61+
# flags.primary-field.summary
62+
63+
Primary field for the dynamic highlights header; typically 'Name'. Used only with RecordPage.
64+
65+
# flags.secondary-fields.summary
66+
67+
Secondary fields shown in the dynamic highlights header. Specify multiple fields separated by commas. Maximum of 11 fields. Used only with RecordPage.
68+
69+
# flags.detail-fields.summary
70+
71+
Fields to display in the Details tab. Specify multiple fields separated by commas. Fields are split into two columns. Used only with RecordPage.
72+
73+
# errors.recordPageRequiresSobject
74+
75+
RecordPage template requires the --sobject flag to specify the Salesforce object API name (e.g., 'Account', 'Opportunity', 'Custom_Object__c').
76+
77+
# errors.tooManySecondaryFields
78+
79+
Too many secondary fields specified (%s). The Dynamic Highlights Panel supports a maximum of %s secondary fields.
80+
81+
# errors.flagRequiresRecordPage
82+
83+
The --%s flag can only be used with --template RecordPage.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
"apex": {
6767
"description": "Create an apex class or trigger."
6868
},
69+
"flexipage": {
70+
"description": "Generate a Lightning FlexiPage from a template."
71+
},
6972
"digital-experience": {
7073
"description": "Create a Digital Experience site."
7174
},
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
* Copyright (c) 2019, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { Flags, loglevel, orgApiVersionFlagWithDeprecations, SfCommand, Ux } from '@salesforce/sf-plugins-core';
9+
import { CreateOutput, FlexipageOptions, TemplateType } from '@salesforce/templates';
10+
import { Messages } from '@salesforce/core';
11+
import { getCustomTemplates, runGenerator } from '../../../../utils/templateCommand.js';
12+
import { internalFlag, outputDirFlag } from '../../../../utils/flags.js';
13+
14+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
15+
const messages = Messages.loadMessages('@salesforce/plugin-templates', 'flexipage');
16+
17+
export default class FlexipageGenerate extends SfCommand<CreateOutput> {
18+
public static readonly summary = messages.getMessage('summary');
19+
public static readonly description = messages.getMessage('description');
20+
public static readonly examples = messages.getMessages('examples');
21+
public static readonly state = 'beta';
22+
public static readonly flags = {
23+
name: Flags.string({
24+
char: 'n',
25+
summary: messages.getMessage('flags.name.summary'),
26+
description: messages.getMessage('flags.name.description'),
27+
required: true,
28+
aliases: ['flexipagename'],
29+
deprecateAliases: true,
30+
}),
31+
template: Flags.option({
32+
char: 't',
33+
summary: messages.getMessage('flags.template.summary'),
34+
required: true,
35+
options: ['RecordPage', 'AppPage', 'HomePage'] as const,
36+
})(),
37+
'output-dir': outputDirFlag,
38+
'api-version': orgApiVersionFlagWithDeprecations,
39+
label: Flags.string({
40+
summary: messages.getMessage('flags.label.summary'),
41+
aliases: ['masterlabel'],
42+
deprecateAliases: true,
43+
}),
44+
description: Flags.string({
45+
summary: messages.getMessage('flags.description.summary'),
46+
}),
47+
sobject: Flags.string({
48+
char: 's',
49+
summary: messages.getMessage('flags.sobject.summary'),
50+
description: messages.getMessage('flags.sobject.description'),
51+
aliases: ['entity-name', 'entity'],
52+
deprecateAliases: true,
53+
}),
54+
'primary-field': Flags.string({
55+
summary: messages.getMessage('flags.primary-field.summary'),
56+
}),
57+
'secondary-fields': Flags.string({
58+
summary: messages.getMessage('flags.secondary-fields.summary'),
59+
multiple: true,
60+
delimiter: ',',
61+
}),
62+
'detail-fields': Flags.string({
63+
summary: messages.getMessage('flags.detail-fields.summary'),
64+
multiple: true,
65+
delimiter: ',',
66+
}),
67+
internal: internalFlag,
68+
loglevel,
69+
};
70+
71+
private static readonly MAX_SECONDARY_FIELDS = 11;
72+
73+
public async run(): Promise<CreateOutput> {
74+
const { flags } = await this.parse(FlexipageGenerate);
75+
76+
// Validate RecordPage requires sobject
77+
if (flags.template === 'RecordPage' && !flags.sobject) {
78+
throw new Error(messages.getMessage('errors.recordPageRequiresSobject'));
79+
}
80+
81+
// Validate RecordPage-specific flags are only used with RecordPage template
82+
const recordPageOnlyFlags = ['primary-field', 'secondary-fields', 'detail-fields'] as const;
83+
if (flags.template !== 'RecordPage') {
84+
for (const flagName of recordPageOnlyFlags) {
85+
if (flags[flagName]) {
86+
throw new Error(messages.getMessage('errors.flagRequiresRecordPage', [flagName]));
87+
}
88+
}
89+
}
90+
91+
// Validate secondary fields limit (Dynamic Highlights Panel supports max 11)
92+
const secondaryFieldsCount = flags['secondary-fields']?.length ?? 0;
93+
if (secondaryFieldsCount > FlexipageGenerate.MAX_SECONDARY_FIELDS) {
94+
throw new Error(
95+
messages.getMessage('errors.tooManySecondaryFields', [
96+
secondaryFieldsCount.toString(),
97+
FlexipageGenerate.MAX_SECONDARY_FIELDS.toString(),
98+
])
99+
);
100+
}
101+
102+
// Convert CLI flags to library options
103+
const flagsAsOptions: FlexipageOptions = {
104+
flexipagename: flags.name,
105+
template: flags.template,
106+
outputdir: flags['output-dir'],
107+
apiversion: flags['api-version'],
108+
masterlabel: flags.label,
109+
description: flags.description,
110+
entityName: flags.sobject,
111+
primaryField: flags['primary-field'],
112+
secondaryFields: flags['secondary-fields'] ?? [],
113+
detailFields: flags['detail-fields'] ?? [],
114+
internal: flags.internal,
115+
};
116+
117+
return runGenerator({
118+
templateType: TemplateType.Flexipage,
119+
opts: flagsAsOptions,
120+
ux: new Ux({ jsonEnabled: this.jsonEnabled() }),
121+
templates: getCustomTemplates(this.configAggregator),
122+
});
123+
}
124+
}

0 commit comments

Comments
 (0)