diff --git a/packages/salesforce/src/deploymentPackage.ts b/packages/salesforce/src/deploymentPackage.ts index 6b6310e8..25138c56 100644 --- a/packages/salesforce/src/deploymentPackage.ts +++ b/packages/salesforce/src/deploymentPackage.ts @@ -541,7 +541,7 @@ export class SalesforcePackage { private normalizeDataForPackage(packagePath: string, data: Buffer | string) { if (XML.isXml(data)) { // Normalize all XML data to avoid SF deployment errors due to excess spaces - return XML.normalize(data); + return XML.normalize(data, { indent: 4, headless: false }); } return data; } diff --git a/packages/util/src/__tests__/xml.test.ts b/packages/util/src/__tests__/xml.test.ts index 8c1f2e45..ce1d5466 100644 --- a/packages/util/src/__tests__/xml.test.ts +++ b/packages/util/src/__tests__/xml.test.ts @@ -36,7 +36,7 @@ describe('xml', () => { $: { attr: '&>' }, '#text': `I&D '100' > '200'` } - }, undefined, { headless: true })).toBe(xmlStr); + }, { headless: true })).toBe(xmlStr); }); }); describe('#parse', () => { @@ -130,6 +130,14 @@ describe('xml', () => { 'test.tag.bar|2'))).toEqual('bar2'); }); }); + describe('#normalize', () => { + it('should retain boolean attributes', () => { + const xml = `` + const expected = ``; + const actual = XML.normalize(xml, { headless: true }); + expect(actual).toEqual(expected); + }); + }); describe('#getNodeTextRange', () => { it('should get node text range on multiple lines', () => { const xml = ` @@ -157,7 +165,7 @@ describe('xml', () => { it('should get node text range of root node', () => { const xml = ` barbar` - expect(XML.getNodeTextRange(xml, 'test')).toEqual( { + expect(XML.getNodeTextRange(xml, 'test')).toEqual({ start: { line: 2, column: 13 }, end: { line: 2, column: 65 } }); diff --git a/packages/util/src/xml.ts b/packages/util/src/xml.ts index f5d40aa1..f0d40083 100644 --- a/packages/util/src/xml.ts +++ b/packages/util/src/xml.ts @@ -42,6 +42,7 @@ export interface XMLParseOptions { export interface XMLStringfyOptions { trimValues?: boolean; headless?: boolean; + indent?: string | number; stripEmptyNodes?: boolean; } @@ -58,14 +59,15 @@ export interface TextRange { export namespace XML { const options: Partial = { - attributeNamePrefix : '', + attributeNamePrefix: '', attributesGroupName: '$', - textNodeName : '#text', + textNodeName: '#text', cdataPropName: '__cdata', // default is 'false' ignoreAttributes : false, - allowBooleanAttributes : true, - parseAttributeValue : true, - removeNSPrefix : false, + allowBooleanAttributes: true, + suppressBooleanAttributes: false, + parseAttributeValue: true, + removeNSPrefix: false, trimValues: true, ignoreDeclaration: false, ignorePiTags: true, @@ -146,15 +148,24 @@ export namespace XML { * @param indent Indent level; if set pretty prints the XML otherwise omits pretty prtining * @returns */ - export function stringify(jsonObj: any, indent?: number | string, options: XMLStringfyOptions = {}) : string { - const indentOptions: Partial = { - format: indent !== undefined, - suppressEmptyNode: options.stripEmptyNodes, - indentBy: indent !== undefined ? typeof indent === 'string' ? indent : ' '.repeat(indent) : undefined, + export function stringify(jsonObj: any, options?: XMLStringfyOptions) : string; + export function stringify(jsonObj: any, indent?: number | string, options?: XMLStringfyOptions) : string; + export function stringify(jsonObj: any, indent?: number | string | XMLStringfyOptions, options?: XMLStringfyOptions) : string { + options = typeof indent === 'object' ? indent : options; + indent = typeof indent === 'object' ? undefined : (options?.indent ?? indent); + const indentBy = (indent && typeof indent !== 'object') + ? (typeof indent === 'string' ? indent : ' '.repeat(indent)) + : undefined; + + const builderOptions: Partial = { + format: !!indentBy, + suppressEmptyNode: options?.stripEmptyNodes === true, + indentBy }; - const xmlString = new XMLBuilder({ ...globalStringifyOptions, ...indentOptions }).build(jsonObj); + + const xmlString = new XMLBuilder({ ...globalStringifyOptions, ...builderOptions }).build(jsonObj); if (options?.headless !== true) { - return `\n${xmlString}`; + return `${builderOptions.format ? '\n' : ''}${xmlString}`; } return xmlString; } @@ -193,8 +204,8 @@ export namespace XML { * @param xml XML string or buffer * @returns normalized XML string without comments or line-breaks. */ - export function normalize(xml: string | Buffer) { - return stringify(parse(xml, { trimValues: true })); + export function normalize(xml: string | Buffer, options?: XMLStringfyOptions) { + return stringify(parse(xml, { trimValues: true }), 0, options); } /**