From 83ce8bc05aa8120c5b06dbec1121ec5d0fad8c0c Mon Sep 17 00:00:00 2001 From: Maximilien Date: Fri, 7 Aug 2026 18:05:47 +0200 Subject: [PATCH 1/2] fix: resolve decomposed Permission Set parents by suffix --- src/resolve/adapters/baseSourceAdapter.ts | 9 ++++++--- src/resolve/adapters/mixedContentSourceAdapter.ts | 15 ++++++++++++++- test/resolve/metadataResolver.test.ts | 10 ++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/resolve/adapters/baseSourceAdapter.ts b/src/resolve/adapters/baseSourceAdapter.ts index 6d75ad4726..65ed3ceeea 100644 --- a/src/resolve/adapters/baseSourceAdapter.ts +++ b/src/resolve/adapters/baseSourceAdapter.ts @@ -110,9 +110,12 @@ export abstract class BaseSourceAdapter implements SourceAdapter { const typeDirName = basename(this.type.inFolder ? dirname(parentPath) : parentPath); const nameMatchesParent = basename(parentPath) === metaXml.fullName; const inTypeDir = typeDirName === this.type.directoryName; - // if the parent folder name matches the fullName OR parent folder name is - // metadata type's directory name, it's a root metadata xml. - isRootMetadataXml = nameMatchesParent || inTypeDir; + const rootSuffixes = [this.type.suffix, this.type.legacySuffix].filter(Boolean); + const suffixMatchesRoot = rootSuffixes.includes(metaXml.suffix); + // Decomposed children can share the parent fullName (for example, + // MyPermissionSet.applicationVisibility-meta.xml). The suffix must + // identify the parent before the directory name can confirm it. + isRootMetadataXml = suffixMatchesRoot && (nameMatchesParent || inTypeDir); } else { isRootMetadataXml = true; } diff --git a/src/resolve/adapters/mixedContentSourceAdapter.ts b/src/resolve/adapters/mixedContentSourceAdapter.ts index c6fba24859..550015da68 100644 --- a/src/resolve/adapters/mixedContentSourceAdapter.ts +++ b/src/resolve/adapters/mixedContentSourceAdapter.ts @@ -16,7 +16,7 @@ import { dirname, basename, sep, join } from 'node:path'; import { Messages } from '@salesforce/core/messages'; import { SfError } from '@salesforce/core/sfError'; -import { baseName } from '../../utils/path'; +import { baseName, parseMetadataXml } from '../../utils/path'; import { SourcePath } from '../../common/types'; import { SourceComponent } from '../sourceComponent'; import { BaseSourceAdapter } from './baseSourceAdapter'; @@ -55,6 +55,19 @@ export class MixedContentSourceAdapter extends BaseSourceAdapter { protected getRootMetadataXmlPath(trigger: SourcePath): SourcePath | undefined { if (this.ownFolder) { const componentRoot = this.trimPathToContent(trigger); + + const rootSuffixes = [this.type.suffix, this.type.legacySuffix].filter( + (suffix): suffix is string => typeof suffix === 'string' + ); + const rootFile = this.tree.readDirectory(componentRoot).find((entry) => { + const metadata = parseMetadataXml(join(componentRoot, entry)); + return metadata?.suffix !== undefined && rootSuffixes.includes(metadata.suffix); + }); + + if (rootFile) { + return join(componentRoot, rootFile); + } + return this.tree.find('metadataXml', basename(componentRoot), componentRoot); } return this.findMetadataFromContent(trigger); diff --git a/test/resolve/metadataResolver.test.ts b/test/resolve/metadataResolver.test.ts index 8ab5ac9732..a5ebb0785b 100644 --- a/test/resolve/metadataResolver.test.ts +++ b/test/resolve/metadataResolver.test.ts @@ -183,6 +183,16 @@ describe('MetadataResolver', () => { expect(components2[0].type.name).to.equal('PermissionSet'); }); + it('Should resolve the Beta2 Permission Set parent when resolving its directory', () => { + const resolver = new MetadataResolver(regAccPermissionSet, SOURCE_FORMAT_PS.tree); + const permissionSetDirectory = join('main', 'default', 'permissionsets', 'myPS'); + const components = resolver.getComponentsFromPath(permissionSetDirectory); + + expect(components).to.have.lengthOf(1); + expect(components[0].type.name).to.equal('PermissionSet'); + expect(components[0].xml).to.equal(join(permissionSetDirectory, 'myPS.permissionset-meta.xml')); + }); + it('Should determine type for metadata file with known suffix and strictDirectoryName', () => { // CustomSite is an example. The conditions are: // 1. Type has "strictDirectoryName": true From ff60f3976f9c356df0791548d716dd2d0232dc24 Mon Sep 17 00:00:00 2001 From: Willie Ruemmele Date: Thu, 20 Aug 2026 11:25:20 -0600 Subject: [PATCH 2/2] chore: bump @salesforce/core to ^9.1.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5dd60514d1..6bbef69e0d 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "node": ">=22.0.0" }, "dependencies": { - "@salesforce/core": "^9.0.0", + "@salesforce/core": "^9.1.4", "@salesforce/kit": "^4.0.0", "@salesforce/ts-types": "^3.0.0", "@salesforce/types": "^1.6.0",