Skip to content

Commit 53d9f7a

Browse files
committed
feat: support token replacement after adding files to a package
1 parent 6e085fe commit 53d9f7a

File tree

8 files changed

+123
-74
lines changed

8 files changed

+123
-74
lines changed

packages/salesforce/src/__tests__/salesforcePackageBuilder.test.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ describe('SalesforcePackageBuilder', () => {
7474
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
7575
await packageBuilder.addFiles([ 'src/lwc/test/test.js-meta.xml']);
7676

77-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
77+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
7878
const manifest = packageBuilder.getManifest().toJson(apiVersion);
7979

8080
expect(filesAdded.length).toEqual(4);
@@ -93,7 +93,7 @@ describe('SalesforcePackageBuilder', () => {
9393
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
9494
await packageBuilder.addFiles([ 'src/lwc/test/test.html']);
9595

96-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
96+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
9797
const manifest = packageBuilder.getManifest().toJson(apiVersion);
9898

9999
expect(filesAdded.length).toEqual(4);
@@ -112,7 +112,7 @@ describe('SalesforcePackageBuilder', () => {
112112
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
113113
await packageBuilder.addFiles([ 'src/lwc/test/test.js']);
114114

115-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
115+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
116116
const manifest = packageBuilder.getManifest().toJson(apiVersion);
117117

118118
expect(filesAdded.length).toEqual(4);
@@ -131,7 +131,7 @@ describe('SalesforcePackageBuilder', () => {
131131
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
132132
await packageBuilder.addFiles([ 'src/lwc/test' ]);
133133

134-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
134+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
135135
const manifest = packageBuilder.getManifest().toJson(apiVersion);
136136

137137
expect(filesAdded.length).toEqual(4);
@@ -156,7 +156,7 @@ describe('SalesforcePackageBuilder', () => {
156156
'src/lwc/test/support.js'
157157
]);
158158

159-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
159+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
160160
const manifest = packageBuilder.getManifest().toJson(apiVersion);
161161

162162
expect(filesAdded.length).toEqual(4);
@@ -177,7 +177,7 @@ describe('SalesforcePackageBuilder', () => {
177177
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
178178
await packageBuilder.addFiles([ 'src/aura/test/test.cmp']);
179179

180-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
180+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
181181
const manifest = packageBuilder.getManifest().toJson(apiVersion);
182182

183183
expect(filesAdded.length).toEqual(3);
@@ -195,7 +195,7 @@ describe('SalesforcePackageBuilder', () => {
195195
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
196196
await packageBuilder.addFiles([ 'src/aura/test/testController.js']);
197197

198-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
198+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
199199
const manifest = packageBuilder.getManifest().toJson(apiVersion);
200200

201201
expect(filesAdded.length).toEqual(3);
@@ -213,7 +213,7 @@ describe('SalesforcePackageBuilder', () => {
213213
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
214214
await packageBuilder.addFiles([ 'src/aura/test' ]);
215215

216-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
216+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
217217
const manifest = packageBuilder.getManifest().toJson(apiVersion);
218218

219219
expect(filesAdded.length).toEqual(3);
@@ -233,7 +233,7 @@ describe('SalesforcePackageBuilder', () => {
233233
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
234234
await packageBuilder.addFiles([ 'src/classes/myClass.cls']);
235235

236-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
236+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
237237
const manifest = packageBuilder.getManifest().toJson(apiVersion);
238238

239239
expect(filesAdded.length).toEqual(2);
@@ -263,7 +263,7 @@ describe('SalesforcePackageBuilder', () => {
263263
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
264264
await packageBuilder.addFiles([ 'src/objects/PetersCustomObject__c']);
265265

266-
const sfPackage = await packageBuilder.getPackage().generateArchive();
266+
const sfPackage = await packageBuilder.build().generateArchive();
267267
const packageFiles = Object.keys(sfPackage.files);
268268
const metadata = await sfPackage.file('objects/PetersCustomObject__c.object')?.async('string');
269269
const parsedMetadata = metadata && XML.parse(metadata, { ignoreAttributes: true, ignoreNamespacePrefix: true });
@@ -291,7 +291,7 @@ describe('SalesforcePackageBuilder', () => {
291291
await packageBuilder.addFiles([ 'src/objects/PetersCustomObject__c/fields']);
292292

293293
const manifest = packageBuilder.getManifest().toJson(apiVersion);
294-
const sfPackage = await packageBuilder.getPackage().generateArchive();
294+
const sfPackage = await packageBuilder.build().generateArchive();
295295
const packageFiles = Object.keys(sfPackage.files);
296296
const metadata = await sfPackage.file('objects/PetersCustomObject__c.object')?.async('string');
297297
const parsedMetadata = metadata && XML.parse(metadata, { ignoreAttributes: true, ignoreNamespacePrefix: true });
@@ -331,7 +331,7 @@ describe('SalesforcePackageBuilder', () => {
331331
'main/default/app/settings/BusinessHours.settings'
332332
]);
333333

334-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
334+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
335335
const manifest = packageBuilder.getManifest().toJson(apiVersion);
336336

337337
expect(filesAdded.length).toEqual(3);
@@ -355,7 +355,7 @@ describe('SalesforcePackageBuilder', () => {
355355
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
356356
await packageBuilder.addFiles([ 'src/destructiveChangesPost.xml' ]);
357357

358-
const filesAdded = normalizePath(Object.keys((await packageBuilder.getPackage().generateArchive()).files));
358+
const filesAdded = normalizePath(Object.keys((await packageBuilder.build().generateArchive()).files));
359359
const manifest = packageBuilder.getManifest().toJson(apiVersion);
360360

361361
expect(filesAdded.length).toEqual(2); // includes package.xml
@@ -369,9 +369,9 @@ describe('SalesforcePackageBuilder', () => {
369369
'src/destructiveChangesPost2.xml'
370370
]);
371371

372-
const filesAdded = normalizePath(Object.keys((await packageBuilder.getPackage().generateArchive()).files));
372+
const filesAdded = normalizePath(Object.keys((await packageBuilder.build().generateArchive()).files));
373373
const manifest = packageBuilder.getManifest().toJson(apiVersion);
374-
const destructedClasses = packageBuilder.getPackage().getDestructiveChanges();
374+
const destructedClasses = packageBuilder.build().getDestructiveChanges();
375375

376376
expect(destructedClasses).toEqual([
377377
{
@@ -402,9 +402,9 @@ describe('SalesforcePackageBuilder', () => {
402402
'src/destructiveChanges.xml'
403403
]);
404404

405-
const filesAdded = normalizePath(Object.keys((await packageBuilder.getPackage().generateArchive()).files));
405+
const filesAdded = normalizePath(Object.keys((await packageBuilder.build().generateArchive()).files));
406406
const manifest = packageBuilder.getManifest().toJson(apiVersion);
407-
const destructedClasses = packageBuilder.getPackage().getDestructiveChanges();
407+
const destructedClasses = packageBuilder.build().getDestructiveChanges();
408408

409409
expect(filesAdded.length).toEqual(2);
410410
expect(destructedClasses).toEqual([
@@ -429,7 +429,7 @@ describe('SalesforcePackageBuilder', () => {
429429
'src/destructiveChangesPost.xml'
430430
]);
431431

432-
const filesAdded = normalizePath(Object.keys((await packageBuilder.getPackage().generateArchive()).files));
432+
const filesAdded = normalizePath(Object.keys((await packageBuilder.build().generateArchive()).files));
433433
const manifest = packageBuilder.getManifest().toJson(apiVersion);
434434

435435
expect(filesAdded.length).toEqual(3);
@@ -445,9 +445,9 @@ describe('SalesforcePackageBuilder', () => {
445445
'src/destructiveChangesSingle.xml'
446446
]);
447447

448-
const filesAdded = normalizePath(Object.keys((await packageBuilder.getPackage().generateArchive()).files));
448+
const filesAdded = normalizePath(Object.keys((await packageBuilder.build().generateArchive()).files));
449449
const manifest = packageBuilder.getManifest().toJson(apiVersion);
450-
const destructedClasses = packageBuilder.getPackage().getDestructiveChanges();
450+
const destructedClasses = packageBuilder.build().getDestructiveChanges();
451451
expect(destructedClasses).toEqual([
452452
{
453453
componentName: 'a',
@@ -467,7 +467,7 @@ describe('SalesforcePackageBuilder', () => {
467467
const packageBuilder = new SalesforcePackageBuilder(SalesforcePackageType.deploy, apiVersion, mockFs);
468468
await packageBuilder.addFiles([ 'src' ]);
469469

470-
const filesAdded = normalizePath([...packageBuilder.getPackage().files()]);
470+
const filesAdded = normalizePath([...packageBuilder.build().files()]);
471471
const manifest = packageBuilder.getManifest();
472472

473473
expect(filesAdded).toEqual(expect.arrayContaining([
@@ -493,7 +493,7 @@ describe('SalesforcePackageBuilder', () => {
493493
await packageBuilder.addFiles([ 'src/dashboards' ]);
494494

495495
const manifest = packageBuilder.getManifest();
496-
const [ folder, dashBoard ] = [...packageBuilder.getPackage().sourceFiles()]
496+
const [ folder, dashBoard ] = [...packageBuilder.build().sourceFiles()]
497497
.sort((a,b) => a.packagePath.localeCompare(b.packagePath));
498498

499499
expect(folder.packagePath).toEqual('dashboards/MyFolder-meta.xml');
@@ -512,7 +512,7 @@ describe('SalesforcePackageBuilder', () => {
512512
});
513513
await packageBuilder.addFiles([ 'src' ]);
514514

515-
const buildPackage = packageBuilder.getPackage();
515+
const buildPackage = packageBuilder.build();
516516
const classMetaXml = buildPackage.getPackageData('classes/myClass.cls-meta.xml')?.data?.toString();
517517
const triggeretaXml = buildPackage.getPackageData('triggers/myTrigger.trigger-meta.xml')?.data?.toString();
518518

@@ -529,7 +529,7 @@ describe('SalesforcePackageBuilder', () => {
529529
});
530530
await packageBuilder.addFiles([ 'src' ]);
531531

532-
const buildPackage = packageBuilder.getPackage();
532+
const buildPackage = packageBuilder.build();
533533
const classMetaXml = buildPackage.getPackageData('classes/myClass.cls-meta.xml')?.data?.toString();
534534
const triggeretaXml = buildPackage.getPackageData('triggers/myTrigger.trigger-meta.xml')?.data?.toString();
535535

packages/salesforce/src/deploy/package.ts

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -264,8 +264,8 @@ export class SalesforcePackage {
264264
* @param name Component name
265265
* @returns FS path from which the component was loaded or undefined when not loaded or not in the current package
266266
*/
267-
public getComponentSourceFiles(type: string, name: string) {
268-
return this.componentToSource.get(`${type}.${name}`.toLowerCase());
267+
public getComponentSourceFiles(component: SalesforcePackageComponent) {
268+
return this.componentToSource.get(`${component.componentType}.${component.componentName}`.toLowerCase());
269269
}
270270

271271
/**
@@ -280,21 +280,15 @@ export class SalesforcePackage {
280280
* @returns Array of paths to files included in this package
281281
*/
282282
public files(): Set<string> {
283-
return new Set(Iterable.concat(
284-
Iterable.transform(this.packageData.values(), {
285-
filter: value => !!value.fsPath,
286-
map: value => value.fsPath!
287-
}),
288-
this.sourceFileToComponent.keys()
289-
));
283+
return new Set(this.sourceFileToComponent.keys());
290284
}
291285

292286
/**
293287
* Get a list of paths to files included in this package and the respective files on the file system from which they were generated.
294288
*/
295289
public *sourceFiles() {
296-
for (const [packagePath, { fsPath }] of this.packageData) {
297-
yield { packagePath, fsPath };
290+
for (const [fsPath, component] of this.sourceFileToComponent) {
291+
yield { packagePath: component.packagePath, fsPath };
298292
}
299293
}
300294

@@ -307,6 +301,21 @@ export class SalesforcePackage {
307301
return this.packageData.get(packagePath)?.fsPath;
308302
}
309303

304+
/**
305+
* Retrieves the source files associated with a specific package path.
306+
*
307+
* @param packagePath - The path to the package whose source files are to be retrieved.
308+
* @returns The source files mapped to the component identified by the package entry,
309+
* or `undefined` if the package entry does not exist.
310+
*/
311+
public getSourceFiles(packagePath: string) {
312+
const packageEntry = this.packageData.get(packagePath);
313+
if (!packageEntry) {
314+
return;
315+
}
316+
return this.getComponentSourceFiles(packageEntry);
317+
}
318+
310319
/**
311320
* Returns source file info such as the detected component type as well as package path for each source file.
312321
* @param sourceFile Source file path
@@ -626,8 +635,8 @@ export class SalesforcePackage {
626635
* @param name Name of the component
627636
* @returns Parsed XML metadata associated to the component as defined in the package
628637
*/
629-
public getPackageMetadata<T extends object = Record<string, any>>(type: string, name: string): T | undefined {
630-
const fsPaths = this.getComponentSourceFiles(type, name);
638+
public getPackageMetadata<T extends object = Record<string, any>>(component: SalesforcePackageComponent): T | undefined {
639+
const fsPaths = this.getComponentSourceFiles(component);
631640
if (fsPaths?.length) {
632641
const metaFile = fsPaths.length == 1 ? fsPaths[0] : fsPaths.find(f => f.toLowerCase().endsWith('.xml'));
633642
const metaFileSourceMap = metaFile && this.sourceFileToComponent.get(metaFile);

packages/salesforce/src/deploy/packageBuilder.ts

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,10 @@ export class SalesforcePackageBuilder {
117117
/**
118118
* Default API version to use when no version is specified.
119119
*/
120-
public static defaultApiVersion = '50.0';
120+
public static defaultApiVersion = '60.0';
121121

122-
private readonly mdPackage: SalesforcePackage;
122+
private mdPackage: SalesforcePackage;
123+
private rebuildRequired = false;
123124
private readonly fs: FileSystem;
124125
private readonly parsedFiles = new Set<string>();
125126
private readonly composedData = new Map<string, MetadataObject>();
@@ -151,6 +152,29 @@ export class SalesforcePackageBuilder {
151152
*/
152153
public addReplacement(replacement: ReplacementDetail) {
153154
this.replacements.push(new TokenReplacement(replacement));
155+
this.rebuildRequired = true;
156+
}
157+
158+
/**
159+
* Rebuilds the Salesforce metadata package by reinitializing the package instance,
160+
* clearing any previously parsed files and composed data.
161+
*
162+
* This is automatically called when replacement tokens are added to the package builder after files have been added.
163+
*
164+
* @returns {this} The current instance for method chaining.
165+
*/
166+
public rebuildPackage(token?: CancellationToken): Promise<this> {
167+
const sources = this.mdPackage.files();
168+
if (sources.size === 0) {
169+
// There is no point in rebuilding the package when no files are added
170+
return Promise.resolve(this);
171+
}
172+
this.logger.verbose(`Rebuilding package with ${sources.size} files`);
173+
this.mdPackage = new SalesforcePackage(this.apiVersion);
174+
this.parsedFiles.clear();
175+
this.composedData.clear();
176+
this.rebuildRequired = false;
177+
return this.addFiles(sources, token);
154178
}
155179

156180
/**
@@ -236,7 +260,8 @@ export class SalesforcePackageBuilder {
236260
strategy?: S | (new(...args: any[]) => S),
237261
options?: Parameters<S['getChangedComponents']>[1]
238262
) : Promise<Array<SalesforcePackageComponent>> {
239-
const mdPackage = this.getPackage();
263+
const mdPackage = this.mdPackage;
264+
mdPackage.generateMissingMetaFiles();
240265
const deltaStrategy: DeltaPackageStrategy<T> = typeof strategy === 'function' || !strategy
241266
? (Container.get(this) ?? container).create(strategy ?? RetrieveDeltaStrategy as any) : strategy;
242267

@@ -576,7 +601,7 @@ export class SalesforcePackageBuilder {
576601
strategy?: S | (new(...args: any[]) => S),
577602
options?: Parameters<S['getChangedComponents']>[1]
578603
) {
579-
const mdPackage = this.getPackage();
604+
const mdPackage = await this.build();
580605
const deltaStrategy = typeof strategy === 'function' || !strategy
581606
? (Container.get(this) ?? container).create(strategy ?? RetrieveDeltaStrategy as any) : strategy;
582607
const changedComponents = await deltaStrategy.getChangedComponents(mdPackage, options);
@@ -600,10 +625,32 @@ export class SalesforcePackageBuilder {
600625
return this.mdPackage.components();
601626
}
602627

628+
/**
629+
* Retrieves the list of files included in the current metadata package.
630+
*
631+
* @returns {string[]} An array of file paths representing the files in the package.
632+
*/
633+
public getPackageFiles() {
634+
return this.mdPackage.files();
635+
}
636+
637+
638+
/**
639+
* Gets the SalesforcePackage underlying the builder without rebuilding it.
640+
* @deprecated Use {@link build} instead which also rebuilds the package when required (e.g. when replacement tokens are added).
641+
*/
642+
public getPackage() {
643+
this.mdPackage.generateMissingMetaFiles();
644+
return this.mdPackage;
645+
}
646+
603647
/**
604648
* Gets SalesforcePackage underlying the builder.
605649
*/
606-
public getPackage(): SalesforcePackage {
650+
public async build(token?: CancellationToken): Promise<SalesforcePackage> {
651+
if (this.rebuildRequired) {
652+
await this.rebuildPackage(token);
653+
}
607654
this.mdPackage.generateMissingMetaFiles();
608655
return this.mdPackage;
609656
}

0 commit comments

Comments
 (0)