feat(lightning-out): add template generate lightning-out command @W-24035498@ - #1048
akashdhake wants to merge 12 commits into
Conversation
a27248b to
9bde2a8
Compare
template generate lightning-out commandtemplate generate lightning-out command @W-24035498@
Reads a JSON definition file and scaffolds the LO 2.0 metadata via @salesforce/templates. Always warns that the generated IframeWhiteListUrlSettings REPLACES the org's Trusted Domains list on deploy. Generate-only.
… success output - Rewrite src/commands/template/generate/lightning-out/index.ts to match the shipped @salesforce/templates LightningOutOptions shape (appName/eca.name, no distributionState/oauthScopes/force). Drops the interactive iframe-confirm/cancel path, --force, --no-prompt, --eca-distribution-state, --eca-oauth-scopes, and the old flat --name flag; adds --app-name, --eca-name. Extracts a pure, exported mergeLightningOutInputs() for per-key flag/definition-file precedence with wholesale list-replace for hostDomains/components, and a getSourceApiVersion() helper (SfProject, best-effort) for a CLI-side sourceApiVersion<68 advisory. Surfaces the generator's result.warnings and prints success guidance via this.info. - Rewrite messages/lightningOut.md to the exact key set run() references (verified by grep): drops iframe/force/no-prompt/eca-distribution-state/ eca-oauth-scopes/flags.name/error.missing-inputs keys, adds app-name/eca-name/definition-file summaries, error.definition-file-not-object, warning.unknown-definition-key, warning.source-api-version, and the success.* guidance keys. - Add unit test test/commands/template/generate/lightning-out/index.test.ts covering mergeLightningOutInputs precedence, list-replace semantics, and unknown-key reporting, plus the beta/hidden command metadata. - Regenerate command-snapshot.json and schemas/*.json (oclif `snapshot generate` / `schema generate`) so test:deprecation-policy and test:json-schema stay green against the new flag set; the schema diff for commands other than lightning-out is solely the pre-existing CreateOutput.warnings field already shipped by the linked generator.
…s without warnings field
9bde2a8 to
06d5bb0
Compare
jshackell-sfdc
left a comment
There was a problem hiding this comment.
See my suggestions, comments, and questions.
|
|
||
| # description | ||
|
|
||
| Generates the seven metadata artifact types a Lightning Out 2.0 app requires: LightningOutApp, MyDomain and Security settings, one CorsWhitelistOrigin per host domain, and the External Client Application OAuth trio (ExternalClientApplication, ExtlClntAppGlobalOauthSettings, ExtlClntAppOauthSettings). The command is generate-only; it does not deploy and never contacts an org. |
There was a problem hiding this comment.
| Generates the seven metadata artifact types a Lightning Out 2.0 app requires: LightningOutApp, MyDomain and Security settings, one CorsWhitelistOrigin per host domain, and the External Client Application OAuth trio (ExternalClientApplication, ExtlClntAppGlobalOauthSettings, ExtlClntAppOauthSettings). The command is generate-only; it does not deploy and never contacts an org. | |
| Lightning Out 2.0 is a Salesforce app that you use to embed custom Lightning web components (LWC) into your external, non-Salesforce apps. | |
| This command gets you started by generating into your Salesforce DX project the seven metadata artifact types that a Lightning Out 2.0 app requires: LightningOutApp, MyDomain and Security settings, one CorsWhitelistOrigin per host domain, and the External Client Application OAuth trio (ExternalClientApplication, ExtlClntAppGlobalOauthSettings, ExtlClntAppOauthSettings). The command is generate-only; it doesn't deploy any metadata to an org. |
|
|
||
| # summary | ||
|
|
||
| Generate the metadata scaffold for a Lightning Out 2.0 application. |
There was a problem hiding this comment.
| Generate the metadata scaffold for a Lightning Out 2.0 application. | |
| Generate the required metadata to scaffold a Lightning Out 2.0 app. |
|
|
||
| # flags.app-name.summary | ||
|
|
||
| Metadata API name of the Lightning Out 2.0 app. |
There was a problem hiding this comment.
| Metadata API name of the Lightning Out 2.0 app. | |
| Developer name of the new Lightning Out 2.0 app. |
There was a problem hiding this comment.
Okay? We tend to use "developer name" to specify the unique ID for MD components. Unless "metadata api name" is something different? If so, stick with "metadata api".
|
|
||
| # flags.eca-name.summary | ||
|
|
||
| Metadata API name of the External Client Application (OAuth) associated with the app. |
There was a problem hiding this comment.
| Metadata API name of the External Client Application (OAuth) associated with the app. | |
| Developer name of the External Client Application (OAuth) associated with the app. |
There was a problem hiding this comment.
Same comment as above.
|
|
||
| # flags.runtime.summary | ||
|
|
||
| Runtime the app targets: LWR_CORE or CLWR. CLWR is experimental. |
There was a problem hiding this comment.
I don't understand what this means, so I don't have a suggestion for a better summary. But for starters, it would be good to explain what LWR_CORE and CLWR mean. For example, something like:
"Runtime used to run the Lightning Out app. LWR_CORE is blah blah blah, CLWR is blah blah blah."
For maintenance reasons, I wouldn't mention that CLWR is experimental unless it's going to stay that way forever. Just my $.02.
There was a problem hiding this comment.
Done — dropped "experimental" and expanded both runtimes as you suggested. It now reads:
Runtime the app targets. LWR_CORE serves from your Salesforce org for authenticated users; CLWR serves from an Experience Cloud site (guest access, extra site-deployment step).
| @@ -0,0 +1,99 @@ | |||
| # examples | |||
|
|
|||
| - Generate from individual flags (discoverable via --help; the nested eca block is flattened into --eca-\* flags): | |||
There was a problem hiding this comment.
| - Generate from individual flags (discoverable via --help; the nested eca block is flattened into --eca-\* flags): | |
| - Generate a Lightning Out 2.0 app using individual flags to specify the values: |
There was a problem hiding this comment.
It's standard to find the flag names using --help, so no need to mention it. And I think the note about the eca block is also unnecessary.
|
|
||
| <%= config.bin %> <%= command.id %> --app-name MyLoApp --runtime LWR_CORE --host-domains https://example.com --eca-name MyLoAppEca --eca-contact-email dev@example.com --eca-callback-url https://example.com/cb | ||
|
|
||
| - Generate from a JSON definition file (like "sf org create scratch --definition-file", the whole input set — including the nested eca block — lives in one file): |
There was a problem hiding this comment.
| - Generate from a JSON definition file (like "sf org create scratch --definition-file", the whole input set — including the nested eca block — lives in one file): | |
| - Generate an app using the values in a JSON definition file called lo-def.json: |
|
|
||
| <%= config.bin %> <%= command.id %> --definition-file lo-def.json | ||
|
|
||
| - Generate into a specific directory: |
There was a problem hiding this comment.
| - Generate into a specific directory: | |
| - Generate the app into a specific directory: |
|
|
||
| <%= config.bin %> <%= command.id %> --definition-file lo-def.json --output-dir force-app/main/default | ||
|
|
||
| - Mix a definition file with an override flag (flags win over the file, per key): |
There was a problem hiding this comment.
| - Mix a definition file with an override flag (flags win over the file, per key): | |
| - Generate the app using most of the values from a definition file, but the --host-domains value overrides its equivalent in the file: |
|
|
||
| Generates the seven metadata artifact types a Lightning Out 2.0 app requires: LightningOutApp, MyDomain and Security settings, one CorsWhitelistOrigin per host domain, and the External Client Application OAuth trio (ExternalClientApplication, ExtlClntAppGlobalOauthSettings, ExtlClntAppOauthSettings). The command is generate-only; it does not deploy and never contacts an org. | ||
|
|
||
| Inputs may come from a --definition-file JSON, individual flags, or both — flags take precedence over the file on a per-key basis. All structural validation (required fields, formats) is performed by the underlying generator. |
There was a problem hiding this comment.
| Inputs may come from a --definition-file JSON, individual flags, or both — flags take precedence over the file on a per-key basis. All structural validation (required fields, formats) is performed by the underlying generator. | |
| Inputs may come from a --definition-file JSON, individual flags, or both. Flag values take precedence over the file on a per-key basis. All structural validation (required fields, formats) is performed by the underlying generator. |
srijanakde2001
left a comment
There was a problem hiding this comment.
Review focused on the executable TypeScript and tests; documentation changes were intentionally omitted.
| const dir = outDir('bad-host-domain'); | ||
| const stderr = execCmd( | ||
| 'template generate lightning-out --app-name BadHostApp --eca-name BadHostApp_ECA --runtime LWR_CORE ' + | ||
| `--host-domains http://app.example.com --components c/myButton --eca-contact-email dev@example.com --eca-callback-url https://app.example.com/cb --output-dir ${dir}`, |
There was a problem hiding this comment.
@salesforce/templates v66.16.0 deliberately accepts both HTTP and HTTPS host origins, so this input succeeds and exits 0. As written, ensureExitCode: 1 should make this NUT fail against the released generator. Could we use a genuinely invalid origin such as ftp://app.example.com and assert that the error mentions http or https? If the CLI is intended to require HTTPS, it needs explicit command-layer validation instead.
There was a problem hiding this comment.
Good catch — done. The NUT now uses ftp://app.example.com and asserts the error names the scheme (matches /host domain/i + /http or https/i), since http/https are both valid inputs to the v66.16.0 generator. We're keeping the "recommend, don't enforce" stance (no command-layer HTTPS requirement), which matches the generator's design — it only rejects non-http(s) schemes.
|
|
||
| it('should scaffold exactly the seven artifact types', () => { | ||
| const projectOutDir = path.join(session.project.dir, 'force-app', 'main', 'default'); | ||
| assert.file([ |
There was a problem hiding this comment.
Could we assert representative generated XML contents here, in addition to filenames? For example, verify the runtime, component, and normalized host domain in LightningOutApp, plus the callback URL or contact email in the ECA metadata. The current test would still pass if the command stopped forwarding or misrouted one of those values, as long as files with the expected names were created.
There was a problem hiding this comment.
Added. A new it('should render the input values into the generated metadata ...') now reads the generated XML and asserts: <runtime>, <componentName>, and both <hostDomain> values in LightningOutApp; contactEmail in the .eca artifact; and the callback URL in the .ecaGlblOauth artifact. So a stopped or misrouted value now fails the test even when files with the right names exist.
| const unknownKeys = Object.keys(defn).filter((k) => !known.has(k)); | ||
| const ecaDefn = (defn.eca ?? {}) as Record<string, unknown>; | ||
| const opts: LightningOutOptions = { | ||
| appName: flags['app-name'] ?? (defn.appName as string), |
There was a problem hiding this comment.
These casts allow valid JSON with incorrect value types to bypass this layer. For example, "appName": 123 or "eca": {"contactEmail": 123} reaches the generator, which calls .trim() and throws an internal TypeError such as trim is not a function, rather than an actionable definition-validation error. Could we validate the definition-file shape before constructing LightningOutOptions, or update the generator to handle unknown values safely?
There was a problem hiding this comment.
Fixed. Added validateDefinitionShape(defn, flags) which type-checks the definition-file fields before building LightningOutOptions (new error.definition-file-field-type message: Definition file field "%s" must be %s.). {"appName": 123} or {"eca": {"contactEmail": 123}} now produces an actionable definition-validation error instead of an internal TypeError. Validation is skipped per-key when a flag overrides that field, since flags always arrive as strings and win per-key.
| } | ||
|
|
||
| // Success guidance (suppressed automatically under --json). | ||
| this.log(messages.getMessage('success.next-step', [opts.outputdir ?? '.', opts.outputdir ?? '.'])); |
There was a problem hiding this comment.
Could we shell-quote the output directory in the suggested deploy command? If the command is run with --output-dir "/tmp/Lightning Out", this currently prints sf project deploy start -d /tmp/Lightning Out ...; copying it causes the shell to treat Out as a separate argument. This does not affect generation itself, but it makes the advertised next step fail for valid paths containing spaces.
There was a problem hiding this comment.
Done. Added shellQuoteArg(), and success.next-step now single-quotes the output dir when it contains whitespace or quote chars — --output-dir "/tmp/Lightning Out" now prints a copy-pasteable ... --source-dir '/tmp/Lightning Out' .... (Also switched the suggested command from -d to --source-dir.)
| callbackUrl: flags['eca-callback-url'] ?? (ecaDefn.callbackUrl as string), | ||
| }, | ||
| outputdir: flags['output-dir'], | ||
| apiversion: flags['api-version'], |
There was a problem hiding this comment.
What behavior is --api-version intended to control here? The v66.16.0 Lightning Out generator accepts apiversion, but the generated templates do not use it, while the floor warning below checks only sfdx-project.json. Consequently, a v70 project with --api-version 67.0 receives no warning, and a v67 project with --api-version 70.0 still receives one. Could we either validate the effective version as flags["api-version"] ?? sourceApiVersion, or remove the flag if it is not intended to affect this command?
There was a problem hiding this comment.
Removed the flag. Investigating confirmed it's inert for this command: none of the seven LO scaffold artifacts carry an <apiVersion> element, and the v66.16.0 generator never reads apiversion — so it couldn't affect output, which is exactly the inconsistency you flagged. Dropped --api-version from the command and from command-snapshot.json. The deploy floor is handled separately (the hardcoded --api-version 68.0 in the suggested deploy command + the sourceApiVersion floor warning), so that guidance is unaffected.
There was a problem hiding this comment.
Follow-up in c8a4317: refined the printed deploy command so it no longer hardcodes --api-version 68.0 on every run. Since 68.0 is the deploy floor (not a recommended version), pinning it would downgrade a project already on a higher version. Now the suggestion includes --api-version 68.0 only when the project's sourceApiVersion is actually below the floor (same condition that fires the floor warning); at/above floor it's omitted so the project default applies. Added a NUT covering both branches.
…tion, drop inert --api-version
|
@jshackell-sfdc Thanks for the thorough copy pass — applied all the wording suggestions in commit 5272b27 (examples, summary, description, and the flag summaries). Two intentional divergences from the literal suggestions, both explained inline on their threads:
|
|
|
||
| # summary | ||
|
|
||
| Generate the required metadata to scaffold a Lightning Out 2.0 app. |
There was a problem hiding this comment.
Terminology — confirm product name. Use the approved external name "Lightning Out" ("2.0" is internal shorthand for the GA-vs-Beta distinction). Please confirm before merge. The same change applies to lines 3, 33, and 61 (lines 25 and 27 are already covered by the suggestions there).
| Generate the required metadata to scaffold a Lightning Out 2.0 app. | |
| Generate the required metadata to scaffold a Lightning Out app. |
There was a problem hiding this comment.
Discussed this naming with our PM (Sangeeta): we're keeping Lightning Out 2.0 until the beta version is completely sunset. Otherwise devs may take "Lightning Out" to be the beta, or be unsure whether it's the beta or the latest. Once beta is deprecated we'll drop the "2.0" and use the name as-is. Leaving it unchanged for now.
|
|
||
| # description | ||
|
|
||
| Lightning Out 2.0 is a Salesforce app that you use to embed custom Lightning web components (LWC) into your external, non-Salesforce apps. |
There was a problem hiding this comment.
Accuracy + grammar. "is a Salesforce app" conflates the product with the LightningOutApp this command generates; "(LWC)" should be plural.
| Lightning Out 2.0 is a Salesforce app that you use to embed custom Lightning web components (LWC) into your external, non-Salesforce apps. | |
| Lightning Out lets you embed custom Lightning web components (LWCs) into your external, non-Salesforce apps. |
There was a problem hiding this comment.
Done — reworded to "Lightning Out 2.0 lets you embed custom Lightning web components (LWCs) into your external, non-Salesforce apps." (fixed the "is a Salesforce app" phrasing and (LWC) → (LWCs)). Kept the "2.0" per the naming note on the first thread.
|
|
||
| Lightning Out 2.0 is a Salesforce app that you use to embed custom Lightning web components (LWC) into your external, non-Salesforce apps. | ||
|
|
||
| This command gets you started by generating into your Salesforce DX project the seven metadata artifact types that a Lightning Out 2.0 app requires: LightningOutApp, MyDomain and Security settings, one CorsWhitelistOrigin per host domain, and the External Client Application OAuth trio (ExternalClientApplication, ExtlClntAppGlobalOauthSettings, ExtlClntAppOauthSettings). The command is generate-only; it doesn't deploy any metadata to an org. |
There was a problem hiding this comment.
Clarity. Move the target ("into your Salesforce DX project") to the end, and replace the informal "trio."
| This command gets you started by generating into your Salesforce DX project the seven metadata artifact types that a Lightning Out 2.0 app requires: LightningOutApp, MyDomain and Security settings, one CorsWhitelistOrigin per host domain, and the External Client Application OAuth trio (ExternalClientApplication, ExtlClntAppGlobalOauthSettings, ExtlClntAppOauthSettings). The command is generate-only; it doesn't deploy any metadata to an org. | |
| This command gets you started by generating the seven metadata artifact types that a Lightning Out app requires into your Salesforce DX project: LightningOutApp, MyDomain and Security settings, one CorsWhitelistOrigin per host domain, and the three External Client Application OAuth components (ExternalClientApplication, ExtlClntAppGlobalOauthSettings, ExtlClntAppOauthSettings). The command is generate-only; it doesn't deploy any metadata to an org. |
There was a problem hiding this comment.
Done — moved "into your Salesforce DX project" to the end and replaced "OAuth trio" with "three External Client Application OAuth components". Kept "a Lightning Out 2.0 app requires" per the naming note.
|
|
||
| # flags.host-domains.summary | ||
|
|
||
| HTTP or HTTPS origin of an external host page that embeds the app; can be specified multiple times. Replaces, rather than merges with, any hostDomains specified in the --definition-file. |
There was a problem hiding this comment.
Active voice (CLI convention). Use the standard repeat-the-flag phrasing instead of "can be specified multiple times."
| HTTP or HTTPS origin of an external host page that embeds the app; can be specified multiple times. Replaces, rather than merges with, any hostDomains specified in the --definition-file. | |
| HTTP or HTTPS origin of an external host page that embeds the app. Repeat the flag to specify more than one. Replaces, rather than merges with, any hostDomains in the --definition-file. |
There was a problem hiding this comment.
Done — applied verbatim: "…that embeds the app. Repeat the flag to specify more than one. Replaces, rather than merges with, any hostDomains in the --definition-file."
|
|
||
| # flags.components.summary | ||
|
|
||
| Name of a Lightning web component exposed by the app; can be specified multiple times. Replaces, rather than merges with, any components specified in the --definition-file. |
There was a problem hiding this comment.
Active voice (CLI convention). Use the standard repeat-the-flag phrasing.
| Name of a Lightning web component exposed by the app; can be specified multiple times. Replaces, rather than merges with, any components specified in the --definition-file. | |
| Name of a Lightning web component exposed by the app. Repeat the flag to specify more than one. Replaces, rather than merges with, any components in the --definition-file. |
There was a problem hiding this comment.
Done — applied verbatim: "…exposed by the app. Repeat the flag to specify more than one. Replaces, rather than merges with, any components in the --definition-file."
|
|
||
| # flags.eca-name.summary | ||
|
|
||
| Developer name of the External Client Application (OAuth) associated with the app. |
There was a problem hiding this comment.
Consistency. Drop the "(OAuth)" parenthetical so all three External Client Application flags read the same (line 57 doesn't use it).
| Developer name of the External Client Application (OAuth) associated with the app. | |
| Developer name of the External Client Application associated with the app. |
There was a problem hiding this comment.
Done — dropped "(OAuth)": "Developer name of the External Client Application associated with the app."
|
|
||
| # flags.eca-contact-email.summary | ||
|
|
||
| Contact email for the External Client Application (OAuth). |
There was a problem hiding this comment.
Consistency. Match the other External Client Application flags by dropping "(OAuth)".
| Contact email for the External Client Application (OAuth). | |
| Contact email for the External Client Application. |
There was a problem hiding this comment.
Done — dropped "(OAuth)": "Contact email for the External Client Application."
|
Thanks @srijanakde2001 and @subashbabu-pixel — all your comments are addressed in the latest push (message-file copy edits, plus the earlier definition-file type validation and the --api-version change). I've replied inline on each thread; feel free to resolve if it looks good, or re-open anything I missed. |
What
Adds the
sf template generate lightning-outcommand (beta, hidden) — a generate-only scaffolder for Lightning Out 2.0 setup. Writes the 7 LO2 metadata artifacts (via the companion@salesforce/templatesgenerator, PR forcedotcom/salesforcedx-templates#880); deploys nothing itself and never contacts an org.Command layer
--app-name,--eca-name,--runtime(LWR_CORE|CLWR),--host-domains(multiple),--components(multiple),--eca-contact-email,--eca-callback-url,--definition-file,--output-dir,--api-version.--definition-file+ flags into one options object with per-key precedence; list keys (host-domains, components) replace wholesale.sourceApiVersion < 68deploy-floor warning and flags unknown--definition-filekeys; prints post-generate deploy guidance.Testing
iframeWhiteListUrlSettingsis absent; validation failure exits non-zero cleanly.Work item: W-24035498 (@W-24035498@)