Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wr/sccache #1383

Merged
merged 4 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions src/resolve/sourceComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ export class SourceComponent implements MetadataComponent {
private readonly forceIgnore: ForceIgnore;
private markedForDelete = false;
private destructiveChangesType?: DestructiveChangesType;
private pathContentMap = new Map<string, string>();

public constructor(
props: ComponentProperties,
Expand Down Expand Up @@ -187,7 +188,14 @@ export class SourceComponent implements MetadataComponent {
public async parseXml<T extends JsonMap>(xmlFilePath?: string): Promise<T> {
const xml = xmlFilePath ?? this.xml;
if (xml) {
const contents = (await this.tree.readFile(xml)).toString();
let contents: string;
if (this.pathContentMap.has(xml)) {
contents = this.pathContentMap.get(xml) as string;
} else {
contents = (await this.tree.readFile(xml)).toString();
this.pathContentMap.set(xml, contents);
}

const replacements = this.replacements?.[xml] ?? this.parent?.replacements?.[xml];
return this.parseAndValidateXML<T>(
replacements ? await replacementIterations(contents, replacements) : contents,
Expand All @@ -200,7 +208,14 @@ export class SourceComponent implements MetadataComponent {
public parseXmlSync<T extends JsonMap>(xmlFilePath?: string): T {
const xml = xmlFilePath ?? this.xml;
if (xml) {
const contents = this.tree.readFileSync(xml).toString();
let contents: string;
if (this.pathContentMap.has(xml)) {
contents = this.pathContentMap.get(xml) as string;
} else {
contents = this.tree.readFileSync(xml).toString();
this.pathContentMap.set(xml, contents);
}

return this.parseAndValidateXML(contents, xml);
}
return {} as T;
Expand Down
14 changes: 11 additions & 3 deletions test/convert/convertContext/nonDecomposition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
VIRTUAL_DIR,
WORKING_DIR,
} from '../../mock/type-constants/customlabelsConstant';
import { SourceComponent } from '../../../src';

describe('NonDecomposition', () => {
const env = createSandbox();
Expand Down Expand Up @@ -57,11 +58,13 @@ describe('NonDecomposition', () => {
};

const result = await context.nonDecomposition.finalize(nonDecomposed.DEFAULT_DIR, TREE);
// @ts-ignore - because the resulting component will have cache info, and the initial one won't
result[0].component.pathContentMap = new Map();
expect(result).to.deep.equal([{ component, writeInfos }]);
});

it('should return WriterFormats when no local files exist', async () => {
const component = nonDecomposed.COMPONENT_1;
const component = new SourceComponent(nonDecomposed.COMPONENT_1);
const context = new ConvertContext();
const [baseName] = component.fullName.split('.');
const output = join(
Expand Down Expand Up @@ -115,12 +118,13 @@ describe('NonDecomposition', () => {
};

const result = await context.nonDecomposition.finalize(nonDecomposed.DEFAULT_DIR, TREE);

// @ts-ignore - because the resulting component will have cache info, and the initial one won't
result[0].component.pathContentMap = new Map();
expect(result).to.deep.equal([{ component, writeInfos }]);
});

it('should merge 1 updated file', async () => {
const component = nonDecomposed.COMPONENT_1;
const component = new SourceComponent(nonDecomposed.COMPONENT_1, TREE);
const context = new ConvertContext();
const type = component.type;

Expand All @@ -144,6 +148,8 @@ describe('NonDecomposition', () => {
};

const result = await context.nonDecomposition.finalize(nonDecomposed.DEFAULT_DIR, TREE);
// @ts-ignore - because the resulting component will have cache info, and the initial one won't
result[0].component.pathContentMap = new Map();
expect(result).to.deep.equal([{ component, writeInfos }]);
});

Expand Down Expand Up @@ -187,6 +193,8 @@ describe('NonDecomposition', () => {
};

const result = await context.nonDecomposition.finalize(nonDecomposed.DEFAULT_DIR, TREE);
// @ts-ignore - because the resulting component will have cache info, and the initial one won't
result[0].component.pathContentMap = new Map();
expect(result).to.deep.equal([{ component, writeInfos }]);
});
});
59 changes: 54 additions & 5 deletions test/convert/convertContext/recomposition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,37 @@ describe('Recomposition', () => {
});

it('should only read parent xml file once for non-decomposed components with children', async () => {
const component = nonDecomposed.COMPONENT_1;
const readFileSpy = env.spy(component.tree, 'readFile');

const context = new ConvertContext();
context.recomposition.transactionState.set(component.fullName, {
component,
children: new ComponentSet(component.getChildren()),
});

const result = await context.recomposition.finalize();
expect(result).to.deep.equal([
{
component,
writeInfos: [
{
source: new JsToXml({
CustomLabels: {
[XML_NS_KEY]: XML_NS_URL,
labels: [CHILD_1_XML, CHILD_2_XML],
},
}),
output: join('labels', 'CustomLabels.labels'),
},
],
},
]);

expect(readFileSpy.callCount, JSON.stringify(readFileSpy.getCalls(), undefined, 2)).to.equal(0);
});

it('should not read parent once already read', async () => {
const component = nonDecomposed.COMPONENT_1;
const context = new ConvertContext();
context.recomposition.transactionState.set(component.fullName, {
Expand Down Expand Up @@ -151,7 +182,7 @@ describe('Recomposition', () => {
},
]);

expect(readFileSpy.callCount, JSON.stringify(readFileSpy.getCalls(), undefined, 2)).to.equal(1);
expect(readFileSpy.callCount, JSON.stringify(readFileSpy.getCalls(), undefined, 2)).to.equal(0);
});

describe('should only read unique child xml files once per parent for non-decomposed components', () => {
Expand Down Expand Up @@ -201,31 +232,49 @@ describe('Recomposition', () => {
],
},
];
const virtualTree = new VirtualTreeContainer(vDir);
const component = new SourceComponent(
{ name: customLabelsType.name, type: customLabelsType, xml: parentXmlPath1 },
new VirtualTreeContainer(vDir)
virtualTree
);
const component2 = new SourceComponent(
{ name: 'CustomLabels2', type: customLabelsType, xml: parentXmlPath2 },
new VirtualTreeContainer(vDir)
virtualTree
);

it('one main component with multiple parents in transaction state covering all the children', async () => {
const context = new ConvertContext();
const compSet = new ComponentSet();
const readFileSpy = env.spy(virtualTree, 'readFileSync');

component.getChildren().forEach((child) => compSet.add(child));
component2.getChildren().forEach((child) => compSet.add(child));
context.recomposition.transactionState.set(component.fullName, {
component,
children: compSet,
});

const readFileSpy = env.spy(VirtualTreeContainer.prototype, 'readFile');

await context.recomposition.finalize();

expect(readFileSpy.callCount, 'readFile() should only be called twice').to.equal(2);
});

it('once the parent/child file content is cached, it wont read them again', async () => {
const context = new ConvertContext();
const compSet = new ComponentSet();

component.getChildren().forEach((child) => compSet.add(child));
component2.getChildren().forEach((child) => compSet.add(child));
context.recomposition.transactionState.set(component.fullName, {
component,
children: compSet,
});
const readFileSpy = env.spy(virtualTree, 'readFileSync');

await context.recomposition.finalize();

expect(readFileSpy.callCount, 'readFile() should only be called twice').to.equal(0);
});
});
});
});
2 changes: 2 additions & 0 deletions test/resolve/adapters/decomposedSourceAdapter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ describe('DecomposedSourceAdapter', () => {
assert(decomposed.DECOMPOSED_CHILD_COMPONENT_1.xml);
const result = adapter.getComponent(decomposed.DECOMPOSED_CHILD_COMPONENT_1.xml);

// @ts-ignore - this only failed when running 'yarn test' - parent has cache info the result won't
component.parent.pathContentMap = new Map();
expect(result).to.deep.equal(component);
});

Expand Down
10 changes: 6 additions & 4 deletions test/resolve/sourceComponent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
import { join } from 'node:path';
import { assert, expect } from 'chai';
import { assert, config, expect } from 'chai';
import { createSandbox } from 'sinon';
import { Messages, SfError } from '@salesforce/core';
import { decomposed, matchingContentFile, mixedContentDirectory, xmlInFolder } from '../mock';
Expand Down Expand Up @@ -41,6 +41,8 @@ import { DE_METAFILE } from '../mock/type-constants/digitalExperienceBundleConst
import { XML_NS_KEY, XML_NS_URL } from '../../src/common';
import { RegistryTestUtil } from './registryTestUtil';

config.truncateThreshold = 0;

Messages.importMessagesDirectory(__dirname);
const messages = Messages.loadMessages('@salesforce/source-deploy-retrieve', 'sdr');

Expand Down Expand Up @@ -244,7 +246,7 @@ describe('SourceComponent', () => {
});

it('should preserve leading zeroes in node values', async () => {
const component = COMPONENT;
const component = new SourceComponent(COMPONENT);
env
.stub(component.tree, 'readFile')
.resolves(Buffer.from('<MatchingContentFile><test>001</test></MatchingContentFile>'));
Expand All @@ -260,7 +262,7 @@ describe('SourceComponent', () => {
});

it('should parse cdata node values', async () => {
const component = COMPONENT;
const component = new SourceComponent(COMPONENT);
env
.stub(component.tree, 'readFile')
.resolves(Buffer.from('<MatchingContentFile><test><![CDATA[<p>Hello</p>]]></test></MatchingContentFile>'));
Expand All @@ -276,7 +278,7 @@ describe('SourceComponent', () => {
});

it('should parse attributes of nodes', async () => {
const component = COMPONENT;
const component = new SourceComponent(COMPONENT);
env
.stub(component.tree, 'readFile')
.resolves(Buffer.from('<MatchingContentFile a="test"><test>something</test></MatchingContentFile>'));
Expand Down
Loading