Skip to content

Commit d7b50fe

Browse files
authored
Sm/nested-lwc (#618)
* fix: support for nested lwc folders * chore: auto-update metadata coverage in METADATA_SUPPORT.md * test: ut * test: ut for deep empties
1 parent 16374e4 commit d7b50fe

File tree

10 files changed

+122
-10
lines changed

10 files changed

+122
-10
lines changed

METADATA_SUPPORT.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This list compares metadata types found in Salesforce v55 with the [metadata reg
44

55
This repository is used by both the Salesforce CLIs and Salesforce's VSCode Extensions.
66

7-
Currently, there are 456/494 supported metadata types.
7+
Currently, there are 456/495 supported metadata types.
88
For status on any existing gaps, please search or file an issue in the [Salesforce CLI issues only repo](https://github.com/forcedotcom/cli/issues).
99
To contribute a new metadata type, please see the [Contributing Metadata Types to the Registry](./contributing/metadata.md)
1010

@@ -41,6 +41,7 @@ To contribute a new metadata type, please see the [Contributing Metadata Types t
4141
|ApexTrigger|||
4242
|AppAnalyticsSettings|||
4343
|AppExperienceSettings|||
44+
|AppExplorationDataConsent||Not supported, but support could be added|
4445
|AppMenu|||
4546
|ApplicationRecordTypeConfig|||
4647
|ApplicationSubtypeDefinition|||
@@ -164,7 +165,7 @@ To contribute a new metadata type, please see the [Contributing Metadata Types t
164165
|DevHubSettings|||
165166
|DigitalExperience||Not supported, but support could be added (but not for tracking)|
166167
|DigitalExperienceBundle||Not supported, but support could be added (but not for tracking)|
167-
|DigitalExperienceBundleSetting||Not supported, but support could be added (but not for tracking)|
168+
|DigitalExperienceConfig||Not supported, but support could be added (but not for tracking)|
168169
|DiscoveryAIModel|||
169170
|DiscoveryGoal|||
170171
|DiscoverySettings|||
@@ -512,7 +513,6 @@ v56 introduces the following new types. Here's their current level of support
512513

513514
|Metadata Type|Support|Notes|
514515
|:---|:---|:---|
515-
|DigitalExperienceConfig||Not supported, but support could be added (but not for tracking)|
516516

517517
## Additional Types
518518

src/resolve/adapters/mixedContentSourceAdapter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export class MixedContentSourceAdapter extends BaseSourceAdapter {
8181
*/
8282
protected trimPathToContent(path: SourcePath): SourcePath {
8383
const pathParts = path.split(sep);
84-
const typeFolderIndex = pathParts.findIndex((part) => part === this.type.directoryName);
84+
const typeFolderIndex = pathParts.lastIndexOf(this.type.directoryName);
8585
const offset = this.type.inFolder ? 3 : 2;
8686
return pathParts.slice(0, typeFolderIndex + offset).join(sep);
8787
}

src/resolve/metadataResolver.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,12 @@ export class MetadataResolver {
230230
const { directoryName, inFolder } = type;
231231
const parts = dirPath.split(sep);
232232
const folderOffset = inFolder ? 2 : 1;
233-
const typeDirectoryIndex = parts.indexOf(directoryName);
233+
const typeDirectoryIndex = parts.lastIndexOf(directoryName);
234234
if (
235235
typeDirectoryIndex === -1 ||
236236
parts.length - folderOffset <= typeDirectoryIndex ||
237+
// ex: /lwc/folder/lwc/cmp
238+
this.tree.readDirectory(dirPath).includes(type.directoryName) ||
237239
// types with children may want to resolve them individually
238240
type.children
239241
) {

src/resolve/sourceComponent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ export class SourceComponent implements MetadataComponent {
203203
// (report, dashboard, emailTemplate, document) and their folder container types:
204204
// (reportFolder, dashboardFolder, emailFolder, documentFolder)
205205
if (!suffix || inFolder || folderContentType) {
206-
return trimUntil(fsPath, directoryName);
206+
return trimUntil(fsPath, directoryName, true);
207207
}
208208

209209
if (folderType) {

src/utils/path.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,11 @@ export function parentName(fsPath: SourcePath): string {
4747
*
4848
* @param fsPath Path to trim
4949
* @param part Path part to trim up until
50+
* @param untilLast Trim until the *last* occurrence of `part`
5051
*/
51-
export function trimUntil(fsPath: SourcePath, part: string): string {
52+
export function trimUntil(fsPath: SourcePath, part: string, untilLast = false): string {
5253
const parts = fsPath.split(sep);
53-
const partIndex = parts.findIndex((p) => part === p);
54+
const partIndex = untilLast ? parts.lastIndexOf(part) : parts.findIndex((p) => part === p);
5455
if (partIndex === -1) {
5556
return fsPath;
5657
}

test/mock/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ export {
1717
nonDecomposed,
1818
decomposedtoplevel,
1919
nestedTypes,
20+
lwcBundle,
2021
} from './type-constants';

test/mock/type-constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import * as decomposed from './customObjectConstant';
1515
import * as decomposedtoplevel from './customObjectTranslationConstant';
1616
import * as nonDecomposed from './customlabelsConstant';
1717
import * as nestedTypes from './territoryConstant';
18+
import * as lwcBundle from './lwcBundleConstant';
1819

1920
export {
2021
xmlInFolder,
@@ -28,4 +29,5 @@ export {
2829
decomposedtoplevel,
2930
nonDecomposed,
3031
nestedTypes,
32+
lwcBundle,
3133
};
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright (c) 2020, 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+
import { join } from 'path';
8+
import { registry, SourceComponent } from '../../../src';
9+
10+
const type = registry.types.lightningcomponentbundle;
11+
12+
/**
13+
* While LWC *can* be identical to Aura bundles, they may also be deeply nested.
14+
* ex projDir/lwc/folder1/lwc/cmpA
15+
* and projDir/lwc/cmpB
16+
*/
17+
export const TYPE_DIRECTORY = join('path', 'to', type.directoryName, 'folder1', type.directoryName);
18+
const COMPONENT_NAME = 'cmpA';
19+
export const CONTENT_PATH = join(TYPE_DIRECTORY, COMPONENT_NAME);
20+
export const XML_NAME = `${COMPONENT_NAME}.js-meta.xml`;
21+
export const XML_PATH = join(CONTENT_PATH, XML_NAME);
22+
export const SUBTYPE_XML_PATH = join(CONTENT_PATH, `${COMPONENT_NAME}.js-meta.xml`);
23+
export const COMPONENTS = [`${COMPONENT_NAME}.js`, `${COMPONENT_NAME}.css`, `${COMPONENT_NAME}.html`];
24+
export const SOURCE_PATHS = COMPONENTS.map((cmp) => join(CONTENT_PATH, cmp));
25+
26+
export const COMPONENT = SourceComponent.createVirtualComponent(
27+
{
28+
name: COMPONENT_NAME,
29+
type,
30+
xml: XML_PATH,
31+
content: CONTENT_PATH,
32+
},
33+
[
34+
{
35+
dirPath: TYPE_DIRECTORY,
36+
children: [COMPONENT_NAME],
37+
},
38+
{
39+
dirPath: CONTENT_PATH,
40+
children: [XML_NAME, ...COMPONENTS],
41+
},
42+
]
43+
);
44+
45+
export const EMPTY_BUNDLE = SourceComponent.createVirtualComponent(
46+
{
47+
name: COMPONENT_NAME,
48+
type,
49+
xml: XML_PATH,
50+
content: CONTENT_PATH,
51+
},
52+
[
53+
{
54+
dirPath: TYPE_DIRECTORY,
55+
children: [COMPONENT_NAME],
56+
},
57+
{
58+
dirPath: CONTENT_PATH,
59+
children: [],
60+
},
61+
]
62+
);

test/resolve/adapters/bundleSourceAdapter.test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@
66
*/
77

88
import { expect } from 'chai';
9-
import { bundle } from '../../mock';
9+
import { bundle, lwcBundle } from '../../mock';
1010
import { BundleSourceAdapter } from '../../../src/resolve/adapters';
1111
import { CONTENT_PATH } from '../../mock/type-constants/auraBundleConstant';
12+
import { CONTENT_PATH as LWC_CONTENT_PATH } from '../../mock/type-constants/lwcBundleConstant';
1213
import { RegistryAccess } from '../../../src';
1314

14-
describe('BundleSourceAdapter', () => {
15+
describe('BundleSourceAdapter with AuraBundle', () => {
1516
const registryAccess = new RegistryAccess();
1617
const adapter = new BundleSourceAdapter(bundle.COMPONENT.type, registryAccess, undefined, bundle.COMPONENT.tree);
1718

@@ -37,4 +38,35 @@ describe('BundleSourceAdapter', () => {
3738
const randomSource = bundle.SOURCE_PATHS[1];
3839
expect(adapter.getComponent(randomSource)).to.deep.equal(bundle.COMPONENT);
3940
});
41+
42+
describe('deeply nested LWC', () => {
43+
const lwcAdapter = new BundleSourceAdapter(
44+
lwcBundle.COMPONENT.type,
45+
registryAccess,
46+
undefined,
47+
lwcBundle.COMPONENT.tree
48+
);
49+
it('Should return expected SourceComponent when given a root metadata xml path', () => {
50+
expect(lwcAdapter.getComponent(lwcBundle.XML_PATH)).to.deep.equal(lwcBundle.COMPONENT);
51+
});
52+
53+
it('Should return expected SourceComponent when given a lwcBundle directory', () => {
54+
expect(lwcAdapter.getComponent(lwcBundle.CONTENT_PATH)).to.deep.equal(lwcBundle.COMPONENT);
55+
});
56+
57+
it('Should return expected SourceComponent when given a source path', () => {
58+
const randomSource = lwcBundle.SOURCE_PATHS[1];
59+
expect(lwcAdapter.getComponent(randomSource)).to.deep.equal(lwcBundle.COMPONENT);
60+
});
61+
62+
it('Should exclude nested empty bundle directories', () => {
63+
const emptyBundleAdapter = new BundleSourceAdapter(
64+
lwcBundle.EMPTY_BUNDLE.type,
65+
registryAccess,
66+
undefined,
67+
lwcBundle.EMPTY_BUNDLE.tree
68+
);
69+
expect(emptyBundleAdapter.getComponent(LWC_CONTENT_PATH)).to.be.undefined;
70+
});
71+
});
4072
});

test/utils/path.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,18 @@ describe('Path Utils', () => {
3131
it('should return trimmed path up until and including the given part', () => {
3232
expect(trimUntil(root, 'to')).to.equal(join('to', 'whatever'));
3333
});
34+
35+
describe('until last', () => {
36+
const path = join('proj', 'lwc', 'folder1', 'lwc', 'myCmp');
37+
38+
it('should return trimmed until first unless last is requested', () => {
39+
expect(trimUntil(path, 'lwc')).to.equal(join('lwc', 'folder1', 'lwc', 'myCmp'));
40+
});
41+
42+
it('should return trimmed until last if requested', () => {
43+
expect(trimUntil(path, 'lwc', true)).to.equal(join('lwc', 'myCmp'));
44+
});
45+
});
3446
});
3547

3648
describe('parseMetadataXml', () => {

0 commit comments

Comments
 (0)