Skip to content

Conversation

@pyelchurisf
Copy link
Contributor

@pyelchurisf pyelchurisf commented Nov 10, 2025

What does this PR do?

Support for web_app DigitalExperienceBundle without meta.xml files

What issues does this PR fix or reference?

#, @W-20091482@

Functionality Before

  • Only site/content bundles with .digitalExperience-meta.xml were supported
  • web_app bundles were treated as unknown directories

Functionality After

web_app DigitalExperienceBundle components are now recognized by their directory structure alone (without requiring meta.xml files) and deploy successfully with correct FileResponses generated for both the bundle and all child files.

@salesforce-cla
Copy link

Thanks for the contribution! It looks like @pyelchurisf is an internal user so signing the CLA is not required. However, we need to confirm this.

The second if block checking web_app paths can never execute because:
- web_app bundles don't have meta.xml files
- super.parseMetadataXml() returns undefined for web_app paths
- The condition 'xml && isWebAppBaseType(path)' is always false
- Reduced path splitting from 3 times to 1 time
- Removed unused pathParts variable
- Improved code clarity with inline comments
const fileResponses: FileResponseSuccess[] = walkedPaths.map((filePath) => {
const relPath = filePath.replace(component.content! + '/', '');
return {
fullName: `${component.fullName}/${relPath}`,
Copy link
Member

Choose a reason for hiding this comment

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

I think you'll need to use path.join here for windows/unix... double check the other usages of / in these changes as well...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed

// Extract bundle name: web_app/WebApp3
const baseType = pathParts[digitalExperiencesIndex + 1];
const spaceApiName = pathParts[digitalExperiencesIndex + 2];
const bundleName = `${baseType}/${spaceApiName}`;
Copy link
Member

Choose a reason for hiding this comment

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

I know server responses use / but if this is going to a filesystem, you need to use join

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed

* Checks if the given path is a web_app bundle directory.
* web_app bundles don't have metadata XML files and are identified by directory structure.
*/
const isWebAppBundlePath = (path: string): boolean => {
Copy link
Member

Choose a reason for hiding this comment

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

can we export the isWebAppBaseType method from digitalEXperienceSourceADapter to keep it DRY

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed

const pathParts = path.split(sep);
const digitalExperiencesIndex = pathParts.indexOf('digitalExperiences');
// Check if the base type (folder after digitalExperiences) is WEB_APP_BASE_TYPE
return (
Copy link
Member

Choose a reason for hiding this comment

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

NIT: this could simplified a little, like

const isWebAppBaseType = (path: string): boolean => {
  const pathParts = path.split(sep);
  const digitalExperiencesIndex = pathParts.indexOf('digitalExperiences');
  return pathParts[digitalExperiencesIndex + 1] === WEB_APP_BASE_TYPE;
};

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Addressed

- Added new snapshot test for DigitalExperienceBundle with web_app base type
- Fixed merge conversion bug where files were placed at wrong directory level
- Refactored duplicate code in digitalExperienceSourceAdapter
- Added getWebAppBundleDir helper function to eliminate duplication
@pyelchurisf pyelchurisf requested a review from a team as a code owner November 13, 2025 15:37
pyelchurisf and others added 4 commits November 13, 2025 22:17
- Use posix.relative and posix.join to ensure consistent path separators
- Fixes test failure on Windows CI where mixed path separators caused incorrect fullName generation
- Child DigitalExperience components now get correct fullNames like 'web_app/zenith/index.html'
- Retrieve now reports individual files for web_app bundles
- Fixes 'Nothing retrieved' warning when retrieving web_app bundles
- Matches the deploy behavior for consistent file response reporting
@WillieRuemmele
Copy link
Member

✅ : before couldn't retrieve this type, after, retrieved it

 ➜  sf project retrieve start --metadata DigitalExperienceBundle:web_app/WebApp3 
 ›   Warning: @salesforce/plugin-agent is a linked ESM module and cannot be auto-transpiled. Existing compiled source will be used instead.

 ────────────── Retrieving Metadata ──────────────

 Retrieving v60.0 metadata from epic.out.abf7af45c3bd@orgfarm.salesforce.com using the v66.0 SOAP API

 ✔ Preparing retrieve request 7ms
 ✔ Sending request to org 167ms
 ✘ Waiting for the org to respond 773ms
 ◼ Done

 Status: In Progress
 Elapsed Time: 953ms

Error (MetadataTransferError): Metadata API request failed: unpackaged/digitalExperiences/web_app/WebApp3/_meta.json: path is to a directory, expected a file


➜  dreamhouse-lwc git:(main)  hub:(GLOBAL - DevHub) scratch:(deb)
 ➜  pd project retrieve start --metadata DigitalExperienceBundle:web_app/WebApp3 

 ────────────── Retrieving Metadata ──────────────

 Retrieving v60.0 metadata from epic.out.abf7af45c3bd@orgfarm.salesforce.com using the v66.0 SOAP API

 ✔ Preparing retrieve request 4ms
 ✔ Sending request to org 177ms
 ✔ Waiting for the org to respond 735ms
 ✔ Done 0ms

 Status: Succeeded
 Elapsed Time: 918ms


Retrieved Source
┌─────────┬─────────────────┬─────────────────────────┬─────────────────────────────────────────────────────────────────────────────┐
│ State   │ Name            │ Type                    │ Path                                                                        │
├─────────┼─────────────────┼─────────────────────────┼─────────────────────────────────────────────────────────────────────────────┤
│ Created │ web_app/WebApp3 │ DigitalExperienceBundle │ force-app/main/default/digitalExperiences/web_app/WebApp3                   │
│ Created │ web_app/WebApp3 │ DigitalExperienceBundle │ force-app/main/default/digitalExperiences/web_app/WebApp3/package.json      │
│ Created │ web_app/WebApp3 │ DigitalExperienceBundle │ force-app/main/default/digitalExperiences/web_app/WebApp3/public/index.html │
│ Created │ web_app/WebApp3 │ DigitalExperienceBundle │ force-app/main/default/digitalExperiences/web_app/WebApp3/src/App.css       │
│ Created │ web_app/WebApp3 │ DigitalExperienceBundle │ force-app/main/default/digitalExperiences/web_app/WebApp3/src/App.js        │
│ Created │ web_app/WebApp3 │ DigitalExperienceBundle │ force-app/main/default/digitalExperiences/web_app/WebApp3/src/index.css     │
│ Created │ web_app/WebApp3 │ DigitalExperienceBundle │ force-app/main/default/digitalExperiences/web_app/WebApp3/src/index.js      │
└─────────┴─────────────────┴─────────────────────────┴─────────────────────────────────────────────────────────────────────────────┘

@WillieRuemmele WillieRuemmele merged commit d1ec3da into forcedotcom:main Nov 13, 2025
88 of 89 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants