Skip to content

Commit

Permalink
fix: handle nulls/undefined in template literal
Browse files Browse the repository at this point in the history
  • Loading branch information
mshanemc committed Jul 19, 2024
1 parent 9b034b1 commit d94960b
Show file tree
Hide file tree
Showing 13 changed files with 75 additions and 45 deletions.
8 changes: 6 additions & 2 deletions src/client/metadataApiDeploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,9 @@ export class MetadataApiDeploy extends MetadataTransfer<
} catch (err) {
const error = err as Error;
this.logger.debug(
`Error trying to compile/send deploy telemetry data for deploy ID: ${this.id}\nError: ${error.message}`
`Error trying to compile/send deploy telemetry data for deploy ID: ${this.id ?? '<not provided>'}\nError: ${
error.message
}`
);
}
const deployResult = new DeployResult(
Expand Down Expand Up @@ -394,7 +396,9 @@ const warnIfUnmatchedServerResult =

// warn that this component is found in server response, but not in component set
void Lifecycle.getInstance().emitWarning(
`${deployMessage.componentType}, ${deployMessage.fullName}, returned from org, but not found in the local project`
`${deployMessage.componentType ?? '<no component type in deploy message>'}, ${
deployMessage.fullName
}, returned from org, but not found in the local project`
);
}
});
Expand Down
4 changes: 2 additions & 2 deletions src/client/metadataTransfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ export abstract class MetadataTransfer<
this.canceled = false;
const asyncResult = await this.pre();
this.transferId = asyncResult.id;
this.logger.debug(`Started metadata transfer. ID = ${this.id}`);
this.logger.debug(`Started metadata transfer. ID = ${this.id ?? '<no id>'}`);
return asyncResult;
}

Expand Down Expand Up @@ -105,7 +105,7 @@ export abstract class MetadataTransfer<
});

try {
this.logger.debug(`Polling for metadata transfer status. ID = ${this.id}`);
this.logger.debug(`Polling for metadata transfer status. ID = ${this.id ?? '<no id>'}`);
this.logger.debug(`Polling frequency (ms): ${normalizedOptions.frequency.milliseconds}`);
this.logger.debug(`Polling timeout (min): ${normalizedOptions.timeout.minutes}`);
const completedMdapiStatus = (await pollingClient.subscribe()) as unknown as Status;
Expand Down
4 changes: 2 additions & 2 deletions src/collections/componentSetBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@ const logComponents = (logger: Logger, componentSet: ComponentSet): void => {
.map((m) => logger.debug(m));
if (components.length > 20) logger.debug(`(showing 20 of ${componentSet.size} matches)`);

logger.debug(`ComponentSet apiVersion = ${componentSet.apiVersion}`);
logger.debug(`ComponentSet sourceApiVersion = ${componentSet.sourceApiVersion}`);
logger.debug(`ComponentSet apiVersion = ${componentSet.apiVersion ?? '<not set>'}`);
logger.debug(`ComponentSet sourceApiVersion = ${componentSet.sourceApiVersion ?? '<not set>'}`);
};

const getOrgComponentFilter = (
Expand Down
2 changes: 1 addition & 1 deletion src/convert/convertContext/nonDecompositionFinalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ const recompose = (children: Map<string, JsonMap>, parentSourceComponent: Source
const getDefaultOutput = (component: SourceComponent): string => {
const { fullName } = component;
const [baseName] = fullName.split('.');
const output = `${baseName}.${component.type.suffix}${META_XML_SUFFIX}`;
const output = `${baseName}.${component.type.suffix ?? ''}${META_XML_SUFFIX}`;

return join(component.getPackageRelativePath('', 'source'), output);
};
15 changes: 10 additions & 5 deletions src/convert/convertContext/recompositionFinalizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const stateValueToWriterFormat =
source: new JsToXml(await recompose(cache)(stateValue)),
output: join(
stateValue.component.type.directoryName,
`${stateValue.component.fullName}.${stateValue.component.type.suffix}`
`${stateValue.component.fullName}.${stateValue.component.type.suffix ?? ''}`
),
},
],
Expand Down Expand Up @@ -139,10 +139,15 @@ const ensureStateValueWithParent = (
return true;
}
throw new Error(
`The parent component is missing from the recomposition state entry. The children are ${stateValue.children
?.toArray()
.map((c) => c.fullName)
.join(', ')}`
`The parent component is missing from the recomposition state entry. ${
stateValue.children
? `The children are ${stateValue.children
?.toArray()
.map((c) => c.fullName)
.join(', ')}
`
: 'There are no children.'
}`
);
};

Expand Down
9 changes: 6 additions & 3 deletions src/convert/transformers/decomposedMetadataTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,10 @@ const getChildWriteInfos =
return [
{
source,
output: join(dirname(mergeWith.xml), `${entryName}.${childComponent.type.suffix}${META_XML_SUFFIX}`),
output: join(
dirname(mergeWith.xml),
`${entryName}.${ensureString(childComponent.type.suffix)}${META_XML_SUFFIX}`
),
},
];
}
Expand Down Expand Up @@ -259,7 +262,7 @@ const getDefaultOutput = (component: MetadataComponent): SourcePath => {
const childName = tail.length ? tail.join('.') : undefined;
const output = join(
parent?.type.strategies?.decomposition === DecompositionStrategy.FolderPerType ? type.directoryName : '',
`${childName ?? baseName}.${component.type.suffix}${META_XML_SUFFIX}`
`${childName ?? baseName}.${ensureString(component.type.suffix)}${META_XML_SUFFIX}`
);
return join(calculateRelativePath('source')({ self: parent?.type ?? type })(fullName)(baseName), output);
};
Expand Down Expand Up @@ -289,7 +292,7 @@ type ComposedMetadata = { tagKey: string; tagValue: AnyJson; parentType: Metadat
type ComposedMetadataWithChildType = ComposedMetadata & { childType: MetadataType };

type InfoContainer = {
entryName?: string;
entryName: string;
childComponent: MetadataComponent;
/** the parsed xml */
value: JsonMap;
Expand Down
4 changes: 2 additions & 2 deletions src/convert/transformers/defaultMetadataTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const getXmlDestination = (
if (!component.content && !['digitalexperiencebundle'].includes(component.type.id)) {
if (targetFormat === 'metadata') {
if (folderContentType) {
xmlDestination = xmlDestination.replace(`.${suffix}`, '');
xmlDestination = xmlDestination.replace(`.${suffix ?? ''}`, '');
} else if (xmlDestination.includes(META_XML_SUFFIX)) {
xmlDestination = xmlDestination.slice(0, xmlDestination.lastIndexOf(META_XML_SUFFIX));
} else {
Expand All @@ -111,7 +111,7 @@ const getXmlDestination = (
}
} else {
xmlDestination = folderContentType
? xmlDestination.replace(META_XML_SUFFIX, `.${suffix}${META_XML_SUFFIX}`)
? xmlDestination.replace(META_XML_SUFFIX, `.${suffix ?? ''}${META_XML_SUFFIX}`)
: `${xmlDestination}${META_XML_SUFFIX}`;
}
} else if (suffix) {
Expand Down
21 changes: 13 additions & 8 deletions src/convert/transformers/staticResourceMetadataTransformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ export class StaticResourceMetadataTransformer extends BaseMetadataTransformer {

// Zip the static resource from disk to a stream, compressing at level 9.
const zipIt = (): Readable => {
getLogger().debug(`zipping static resource: ${component.content}`);
// static resource should always have content prop
getLogger().debug(`zipping static resource: ${content}`);
const zip = JSZip();

// JSZip does not have an API for adding a directory of files recursively so we always
Expand All @@ -78,7 +79,7 @@ export class StaticResourceMetadataTransformer extends BaseMetadataTransformer {
streamFiles: true,
})
.on('end', () => {
getLogger().debug(`zip complete for: ${component.content}`);
getLogger().debug(`zip complete for: ${content}`);
})
);
};
Expand All @@ -88,7 +89,7 @@ export class StaticResourceMetadataTransformer extends BaseMetadataTransformer {
source: (await componentIsExpandedArchive(component))
? zipIt()
: getReplacementStreamForReadable(component, content),
output: join(type.directoryName, `${baseName(content)}.${type.suffix}`),
output: join(type.directoryName, `${baseName(content)}.${type.suffix ?? ''}`),
},
{
source: getReplacementStreamForReadable(component, xml),
Expand Down Expand Up @@ -197,7 +198,9 @@ const getContentType = async (component: SourceComponent): Promise<string> => {

if (typeof output !== 'string') {
throw new SfError(
`Expected a string for contentType in ${component.name} (${component.xml}) but got ${JSON.stringify(output)}`
`Expected a string for contentType in ${component.name} (${component.xml ?? ''}) but got ${JSON.stringify(
output
)}`
);
}
return output;
Expand All @@ -211,7 +214,7 @@ const getBaseContentPath = (component: SourceComponent, mergeWith?: SourceCompon
const baseContentPath = component.getPackageRelativePath(component.content, 'source');
return join(dirname(baseContentPath), baseName(baseContentPath));
}
throw new SfError(`Expected a content path for ${component.name} (${component.xml})`);
throw new SfError(`Expected a content path for ${component.name} (${component.xml ?? ''})`);
};

const getExtensionFromType = (contentType: string): string =>
Expand All @@ -238,8 +241,10 @@ async function getStaticResourceZip(component: SourceComponent, content: string)
const staticResourceZip = await component.tree.readFile(content);
return await JSZip.loadAsync(staticResourceZip, { createFolders: true });
} catch (e) {
throw new SfError(`Unable to open zip file ${content} for ${component.name} (${component.xml})`, 'BadZipFile', [
'Check that your file really is a valid zip archive',
]);
throw new SfError(
`Unable to open zip file ${content} for ${component.name} (${component.xml ?? ''})`,
'BadZipFile',
['Check that your file really is a valid zip archive']
);
}
}
5 changes: 4 additions & 1 deletion src/resolve/adapters/digitalExperienceSourceAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/
import { dirname, join, sep } from 'node:path';
import { Messages } from '@salesforce/core';
import { ensureString } from '@salesforce/ts-types';
import { META_XML_SUFFIX } from '../../common/constants';
import { SourcePath } from '../../common/types';
import { SourceComponent } from '../sourceComponent';
Expand Down Expand Up @@ -147,7 +148,9 @@ export class DigitalExperienceSourceAdapter extends BundleSourceAdapter {
// 3 because we want 'digitalExperiences' directory, 'baseType' directory and 'bundleName' directory
const basePath = pathParts.slice(0, typeFolderIndex + 3).join(sep);
const bundleFileName = pathParts[typeFolderIndex + 2];
const suffix = this.isBundleType() ? this.type.suffix : this.registry.getParentType(this.type.id)?.suffix;
const suffix = ensureString(
this.isBundleType() ? this.type.suffix : this.registry.getParentType(this.type.id)?.suffix
);
return `${basePath}${sep}${bundleFileName}.${suffix}${META_XML_SUFFIX}`;
}

Expand Down
16 changes: 12 additions & 4 deletions src/resolve/connectionResolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,17 @@ export class ConnectionResolver {

return {
components: Aggregator.filter(componentFilter).map((component) => ({
fullName: ensureString(component.fullName, `Component fullName was not set for ${component.fileName}`),
fullName: ensureString(
component.fullName,
`Component fullName was not set for ${component.fileName ?? '<missing filename>'}`
),
type: this.registry.getTypeByName(
ensureString(component.type, `Component type was not set for ${component.fullName} (${component.fileName})`)
ensureString(
component.type,
`Component type was not set for ${component.fullName ?? '<missing fullname>'} (${
component.fileName ?? '<missing filename>'
})`
)
),
})),
apiVersion: this.connection.getApiVersion(),
Expand Down Expand Up @@ -142,7 +150,7 @@ const listMembers =
return (
standardValueSetRecord.Metadata.standardValue.length && {
fullName: standardValueSetRecord.MasterLabel,
fileName: `${mdType.directoryName}/${standardValueSetRecord.MasterLabel}.${mdType.suffix}`,
fileName: `${mdType.directoryName}/${standardValueSetRecord.MasterLabel}.${mdType.suffix ?? ''}`,
type: mdType.name,
}
);
Expand Down Expand Up @@ -173,7 +181,7 @@ const inferFilenamesFromType =
(metadataType: MetadataType) =>
(member: RelevantFileProperties): RelevantFileProperties =>
typeof member.fileName === 'object'
? { ...member, fileName: `${metadataType.directoryName}/${member.fullName}.${metadataType.suffix}` }
? { ...member, fileName: `${metadataType.directoryName}/${member.fullName}.${metadataType.suffix ?? ''}` }
: member;

const isNonEmptyString = (value: string | undefined): boolean => typeof value === 'string' && value.length > 0;
6 changes: 3 additions & 3 deletions src/resolve/sourceComponent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ export class SourceComponent implements MetadataComponent {
}
if (this.parent && this.type.ignoreParentName) {
if (!this.name) {
throw new SfError(`Component was initialized without a name: ${this.xml} (${this.type.name})`);
throw new SfError(`Component was initialized without a name: ${this.xml ?? ''} (${this.type.name})`);
}
return this.name;
} else {
Expand Down Expand Up @@ -355,12 +355,12 @@ export class SourceComponent implements MetadataComponent {
if (!uniqueIdElement) {
return [];
}
const xmlPathToChildren = `${this.type.name}.${childType.xmlElementName}`;
const xmlPathToChildren = `${this.type.name}.${childType.xmlElementName ?? ''}`;
const elements = ensureArray(get(parsed, xmlPathToChildren, []));
return elements.map((element) => {
const name = getString(element, uniqueIdElement);
if (!name) {
throw new SfError(`Missing ${uniqueIdElement} on ${childType.name} in ${this.xml}`);
throw new SfError(`Missing ${uniqueIdElement} on ${childType.name} in ${this.xml ?? ''}`);
}
return new SourceComponent(
{
Expand Down
24 changes: 13 additions & 11 deletions src/utils/filePathGenerator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const filePathsFromMetadataComponent = (

if (type.strategies?.adapter === 'digitalExperience') {
// child MD Type, the metafile is a JSON, not an XML
if (type.id === 'digitalexperience') {
if (type.id === 'digitalexperience' && type.metaFileSuffix) {
// metafile name = metaFileSuffix for DigitalExperience.
return [
join(
Expand All @@ -55,12 +55,12 @@ export const filePathsFromMetadataComponent = (
}

// parent MD Type
if (type.id === 'digitalexperiencebundle') {
if (type.id === 'digitalexperiencebundle' && type.suffix) {
return [join(packageDirWithTypeDir, `${fullName}${sep}${basename(fullName)}.${type.suffix}${META_XML_SUFFIX}`)];
}
}

if (type.strategies?.adapter === 'decomposed') {
if (type.strategies?.adapter === 'decomposed' && type.suffix) {
return [join(packageDirWithTypeDir, `${fullName}${sep}${fullName}.${type.suffix}${META_XML_SUFFIX}`)];
}

Expand All @@ -70,19 +70,19 @@ export const filePathsFromMetadataComponent = (
}

// Non-decomposed parents (i.e., any type that defines children and not a decomposed transformer)
if (type.children) {
if (type.children && type.suffix) {
return [join(packageDirWithTypeDir, `${fullName}.${type.suffix}${META_XML_SUFFIX}`)];
}

// basic metadata (with or without folders)
if (!type.children && !type.strategies) {
if (!type.children && !type.strategies && type.suffix) {
return (type.inFolder ?? type.folderType ? generateFolders({ fullName, type }, packageDirWithTypeDir) : []).concat([
join(packageDirWithTypeDir, `${fullName}.${type.suffix}${META_XML_SUFFIX}`),
]);
}

// matching content (with or without folders)
if (type.strategies?.adapter === 'matchingContentFile') {
if (type.strategies?.adapter === 'matchingContentFile' && type.suffix) {
return (type.inFolder ? generateFolders({ fullName, type }, packageDirWithTypeDir) : []).concat([
join(packageDirWithTypeDir, `${fullName}.${type.suffix}${META_XML_SUFFIX}`),
join(packageDirWithTypeDir, `${fullName}.${type.suffix}`),
Expand All @@ -102,8 +102,10 @@ export const filePathsFromMetadataComponent = (
return [
join(
packageDirWithTypeDir,
// registry doesn't have a suffix for EB (it comes down inside the mdapi response)
`${fullName}.${type.strategies?.transformer === 'staticResource' ? type.suffix : 'site'}${META_XML_SUFFIX}`
// registry doesn't have a suffix for EB (it comes down inside the mdapi response). // staticResource has a suffix
`${fullName}.${
type.strategies?.transformer === 'staticResource' ? (type.suffix as string) : 'site'
}${META_XML_SUFFIX}`
),
join(packageDirWithTypeDir, `${fullName}`),
];
Expand Down Expand Up @@ -140,7 +142,7 @@ const generateFolders = ({ fullName, type }: MetadataComponent, packageDirWithTy
join(
packageDirWithTypeDir,
`${originalArray.slice(0, index + 1).join(sep)}.${
registryAccess.getTypeByName(folderType).suffix
registryAccess.getTypeByName(folderType).suffix ?? ''
}${META_XML_SUFFIX}`
)
);
Expand All @@ -159,14 +161,14 @@ const getDecomposedChildType = ({ fullName, type }: MetadataComponent, packageDi
// parent
join(
topLevelTypeDir,
`${fullName.split('.')[0]}${sep}${fullName.split('.')[0]}.${topLevelType.suffix}${META_XML_SUFFIX}`
`${fullName.split('.')[0]}${sep}${fullName.split('.')[0]}.${topLevelType.suffix ?? ''}${META_XML_SUFFIX}`
),
// child
join(
topLevelTypeDir,
fullName.split('.')[0],
type.directoryName,
`${fullName.split('.')[1]}.${type.suffix}${META_XML_SUFFIX}`
`${fullName.split('.')[1]}.${type.suffix ?? ''}${META_XML_SUFFIX}`
),
];
};
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ describe('Multiple large custom labels files', () => {
genUniqueDir: false,
});
// Longer than 10 seconds could indicate a regression
expect(Date.now() - convertStartTime, 'conversion should take less than 10 seconds').to.be.lessThan(10000);
expect(Date.now() - convertStartTime, 'conversion should take less than 10 seconds').to.be.lessThan(10_000);

const convertedFiles = await getConvertedFilePaths(testOutput);
for (const file of convertedFiles) {
Expand Down

2 comments on commit d94960b

@svc-cli-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: d94960b Previous: 56814c1 Ratio
eda-componentSetCreate-linux 187 ms 184 ms 1.02
eda-sourceToMdapi-linux 2421 ms 2306 ms 1.05
eda-sourceToZip-linux 1822 ms 1767 ms 1.03
eda-mdapiToSource-linux 2773 ms 2888 ms 0.96
lotsOfClasses-componentSetCreate-linux 348 ms 343 ms 1.01
lotsOfClasses-sourceToMdapi-linux 3631 ms 3732 ms 0.97
lotsOfClasses-sourceToZip-linux 3054 ms 3054 ms 1
lotsOfClasses-mdapiToSource-linux 3484 ms 3532 ms 0.99
lotsOfClassesOneDir-componentSetCreate-linux 625 ms 633 ms 0.99
lotsOfClassesOneDir-sourceToMdapi-linux 6579 ms 6504 ms 1.01
lotsOfClassesOneDir-sourceToZip-linux 5440 ms 5663 ms 0.96
lotsOfClassesOneDir-mdapiToSource-linux 6384 ms 6327 ms 1.01

This comment was automatically generated by workflow using github-action-benchmark.

@svc-cli-bot
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmark

Benchmark suite Current: d94960b Previous: 56814c1 Ratio
eda-componentSetCreate-win32 408 ms 489 ms 0.83
eda-sourceToMdapi-win32 4575 ms 5041 ms 0.91
eda-sourceToZip-win32 2805 ms 3395 ms 0.83
eda-mdapiToSource-win32 5912 ms 6959 ms 0.85
lotsOfClasses-componentSetCreate-win32 920 ms 1042 ms 0.88
lotsOfClasses-sourceToMdapi-win32 7858 ms 9154 ms 0.86
lotsOfClasses-sourceToZip-win32 4935 ms 5801 ms 0.85
lotsOfClasses-mdapiToSource-win32 7897 ms 9404 ms 0.84
lotsOfClassesOneDir-componentSetCreate-win32 1442 ms 1826 ms 0.79
lotsOfClassesOneDir-sourceToMdapi-win32 13253 ms 16272 ms 0.81
lotsOfClassesOneDir-sourceToZip-win32 8584 ms 9961 ms 0.86
lotsOfClassesOneDir-mdapiToSource-win32 13298 ms 14944 ms 0.89

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.