@salesforce/source-deploy-retrieve
Version:
JavaScript library to run Salesforce metadata deploys and retrieves
147 lines • 14.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.filePathsFromMetadataComponent = void 0;
/*
* Copyright (c) 2020, salesforce.com, inc.
* All rights reserved.
* Licensed under the BSD 3-Clause license.
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
*/
const node_path_1 = require("node:path");
const core_1 = require("@salesforce/core");
const ts_types_1 = require("@salesforce/ts-types");
const constants_1 = require("../common/constants");
const registryAccess_1 = require("../registry/registryAccess");
;
const messages = new core_1.Messages('@salesforce/source-deploy-retrieve', 'sdr', new Map([["md_request_fail", "Metadata API request failed: %s"], ["error_convert_invalid_format", "Invalid conversion format '%s'"], ["error_could_not_infer_type", "%s: Could not infer a metadata type"], ["error_unexpected_child_type", "Unexpected child metadata [%s] found for parent type [%s]"], ["noParent", "Could not find parent type for %s (%s)"], ["error_expected_source_files", "%s: Expected source files for type '%s'"], ["error_failed_convert", "Component conversion failed: %s"], ["error_merge_metadata_target_unsupported", "Merge convert for metadata target format currently unsupported"], ["error_missing_adapter", "Missing adapter '%s' for metadata type '%s'"], ["error_missing_transformer", "Missing transformer '%s' for metadata type '%s'"], ["error_missing_type_definition", "Missing metadata type definition in registry for id '%s'."], ["error_missing_child_type_definition", "Type %s does not have a child type definition %s."], ["noChildTypes", "No child types found in registry for %s (reading %s at %s)"], ["error_no_metadata_xml_ignore", "Metadata xml file %s is forceignored but is required for %s."], ["noSourceIgnore", "%s metadata types require source files, but %s is forceignored."], ["noSourceIgnore.actions", "- Metadata types with content are composed of two files: a content file (ie MyApexClass.cls) and a -meta.xml file (i.e MyApexClass.cls-meta.xml). You must include both files in your .forceignore file. Or try appending \u201C\\*\u201D to your existing .forceignore entry.\n\nSee <https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm> for examples"], ["error_path_not_found", "%s: File or folder not found"], ["noContentFound", "SourceComponent %s (metadata type = %s) is missing its content file."], ["noContentFound.actions", ["Ensure the content file exists in the expected location.", "If the content file is in your .forceignore file, ensure the meta-xml file is also ignored to completely exclude it."]], ["error_parsing_xml", "SourceComponent %s (metadata type = %s) does not have an associated metadata xml to parse"], ["error_expected_file_path", "%s: path is to a directory, expected a file"], ["error_expected_directory_path", "%s: path is to a file, expected a directory"], ["error_directory_not_found_or_not_directory", "%s: path is not a directory"], ["error_no_directory_stream", "%s doesn't support readable streams on directories."], ["error_no_source_to_deploy", "No source-backed components present in the package."], ["error_no_components_to_retrieve", "No components in the package to retrieve."], ["error_static_resource_expected_archive_type", "A StaticResource directory must have a content type of application/zip or application/jar - found %s for %s."], ["error_static_resource_missing_resource_file", "A StaticResource must have an associated .resource file, missing %s.resource-meta.xml"], ["error_no_job_id", "The %s operation is missing a job ID. Initialize an operation with an ID, or start a new job."], ["missingApiVersion", "Could not determine an API version to use for the generated manifest. Tried looking for sourceApiVersion in sfdx-project.json, apiVersion from config vars, and the highest apiVersion from the APEX REST endpoint. Using API version 58.0 as a last resort."], ["invalid_xml_parsing", "error parsing %s due to:\\n message: %s\\n line: %s\\n code: %s"], ["zipBufferError", "Zip buffer was not created during conversion"], ["undefinedComponentSet", "Unable to construct a componentSet. Check the logs for more information."], ["replacementsFileNotRead", "The file \"%s\" specified in the \"replacements\" property of sfdx-project.json could not be read."], ["unsupportedBundleType", "Unsupported Bundle Type: %s"], ["filePathGeneratorNoTypeSupport", "Type not supported for filepath generation: %s"], ["missingFolderType", "The registry has %s as is inFolder but it does not have a folderType"], ["tooManyFiles", "Multiple files found for path: %s."], ["cantGetName", "Unable to calculate fullName from path: %s (%s)"], ["missingMetaFileSuffix", "The metadata registry is configured incorrectly for %s. Expected a metaFileSuffix."], ["uniqueIdElementNotInRegistry", "No uniqueIdElement found in registry for %s (reading %s at %s)."], ["uniqueIdElementNotInChild", "The uniqueIdElement %s was not found the child (reading %s at %s)."], ["suggest_type_header", "A metadata type lookup for \"%s\" found the following close matches:"], ["suggest_type_did_you_mean", "-- Did you mean \".%s%s\" instead for the \"%s\" metadata type?"], ["suggest_type_more_suggestions", "Additional suggestions:\nConfirm the file name, extension, and directory names are correct. Validate against the registry at:\n<https://github.com/forcedotcom/source-deploy-retrieve/blob/main/src/registry/metadataRegistry.json>\n\nIf the type is not listed in the registry, check that it has Metadata API support via the Metadata Coverage Report:\n<https://developer.salesforce.com/docs/metadata-coverage>\n\nIf the type is available via Metadata API but not in the registry\n\n- Open an issue <https://github.com/forcedotcom/cli/issues>\n- Add the type via PR. Instructions: <https://github.com/forcedotcom/source-deploy-retrieve/blob/main/contributing/metadata.md>"], ["type_name_suggestions", "Confirm the metadata type name is correct. Validate against the registry at:\n<https://github.com/forcedotcom/source-deploy-retrieve/blob/main/src/registry/metadataRegistry.json>\n\nIf the type is not listed in the registry, check that it has Metadata API support via the Metadata Coverage Report:\n<https://developer.salesforce.com/docs/metadata-coverage>\n\nIf the type is available via Metadata API but not in the registry\n\n- Open an issue <https://github.com/forcedotcom/cli/issues>\n- Add the type via PR. Instructions: <https://github.com/forcedotcom/source-deploy-retrieve/blob/main/contributing/metadata.md>"]]));
const registryAccess = new registryAccess_1.RegistryAccess();
/**
* Provided a metadata fullName and type pair, return an array of file paths that should
* be expected based on the type's definition in the metadata registry.
*
* This won't give all the filenames for decomposed types (that would require retrieving
* the actual parent xml) but should provide enough of the filePath to figure out if the
* forceignore would ignore it.
*
* Example:
* `const type = new RegistryAccess().getTypeByName('ApexClass');`
* `filePathsFromMetadataComponent({ fullName: 'MyClass', type }, 'myPackageDir');`
* returns:
* `['myPackageDir/classes/MyClass.cls', 'myPackageDir/classes/MyClass.cls-meta.xml']`
*
* @param param a MetadataComponent (type/name pair) for which to generate file paths
* @param packageDir optional package directory to apply to the file paths
* @returns array of file paths
*/
// eslint-disable-next-line complexity
const filePathsFromMetadataComponent = ({ fullName, type }, packageDir) => {
const packageDirWithTypeDir = packageDir ? (0, node_path_1.join)(packageDir, type.directoryName) : type.directoryName;
if (type.strategies?.adapter === 'digitalExperience') {
// child MD Type, the metafile is a JSON, not an XML
if (type.id === 'digitalexperience' && type.metaFileSuffix) {
// metafile name = metaFileSuffix for DigitalExperience.
return [
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName.split('.')[0]}${node_path_1.sep}${fullName.split('.')[1]}${node_path_1.sep}${type.metaFileSuffix}`),
];
}
// parent MD Type
if (type.id === 'digitalexperiencebundle' && type.suffix) {
return [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}${(0, node_path_1.basename)(fullName)}.${type.suffix}${constants_1.META_XML_SUFFIX}`)];
}
}
if (type.strategies?.adapter === 'decomposed' && type.suffix) {
return [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}${fullName}.${type.suffix}${constants_1.META_XML_SUFFIX}`)];
}
// this needs to be done before the other types because of potential overlaps
if (!type.children && Object.keys(registryAccess.getRegistry().childTypes).includes(type.id)) {
return getDecomposedChildType({ fullName, type }, packageDir);
}
// Non-decomposed parents (i.e., any type that defines children and not a decomposed transformer)
if (type.children && type.suffix) {
return [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}.${type.suffix}${constants_1.META_XML_SUFFIX}`)];
}
// basic metadata (with or without folders)
if (!type.children && type.suffix && (!type.strategies || type.strategies.transformer === 'decomposedLabels')) {
return (type.inFolder ?? type.folderType ? generateFolders({ fullName, type }, packageDirWithTypeDir) : []).concat([
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}.${type.suffix}${constants_1.META_XML_SUFFIX}`),
]);
}
// matching content (with or without folders)
if (type.strategies?.adapter === 'matchingContentFile' && type.suffix) {
return (type.inFolder ? generateFolders({ fullName, type }, packageDirWithTypeDir) : []).concat([
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}.${type.suffix}${constants_1.META_XML_SUFFIX}`),
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}.${type.suffix}`),
]);
}
// mixed content in folder (ex: document)
if (type.strategies?.adapter === 'mixedContent' && type.inFolder && !type.strategies.transformer) {
return generateFolders({ fullName, type }, packageDirWithTypeDir).concat([
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${constants_1.META_XML_SUFFIX}`),
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}`),
]);
}
// mixed content not in folder (ex: staticResource,experienceBundle)
if (type.strategies?.adapter === 'mixedContent' && !type.inFolder) {
return [
(0, node_path_1.join)(packageDirWithTypeDir,
// 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 : 'site'}${constants_1.META_XML_SUFFIX}`),
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}`),
];
}
// lwc, aura, waveTemplate, experiencePropertyType, lightningTypeBundle, contentTypeBundle
if (type.strategies?.adapter === 'bundle') {
const mappings = new Map([
['ExperiencePropertyTypeBundle', [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}schema.json`)]],
['LightningTypeBundle', [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}schema.json`)]],
['ContentTypeBundle', [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}schema.json`)]],
['WaveTemplateBundle', [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}template-info.json`)]],
['LightningComponentBundle', [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}${fullName}.js${constants_1.META_XML_SUFFIX}`)]],
['AuraDefinitionBundle', [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}${fullName}.cmp${constants_1.META_XML_SUFFIX}`)]],
['GenAiFunction', [(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}${fullName}.genAiFunction${constants_1.META_XML_SUFFIX}`)]],
[
'GenAiPlannerBundle',
[(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}${fullName}.genAiPlannerBundle${constants_1.META_XML_SUFFIX}`)],
],
[
'AppFrameworkTemplateBundle',
[
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}template-info.json`),
(0, node_path_1.join)(packageDirWithTypeDir, `${fullName}${node_path_1.sep}layout.json`),
],
],
]);
const matched = mappings.get(type.name);
if (!matched) {
throw messages.createError('unsupportedBundleType', [type.name]);
}
return matched;
}
throw messages.createError('filePathGeneratorNoTypeSupport', [type.name]);
};
exports.filePathsFromMetadataComponent = filePathsFromMetadataComponent;
const generateFolders = ({ fullName, type }, packageDirWithTypeDir) => {
const folderType = type.folderType;
if (!folderType) {
throw messages.createError('missingFolderType', [type.name]);
}
// create a folder for each part of the filename between the directory name and the fullname
const splits = fullName.split('/');
return splits
.slice(0, splits.length - 1) // the last one is not a folder
.map((value, index, originalArray) => (0, node_path_1.join)(packageDirWithTypeDir, `${originalArray.slice(0, index + 1).join(node_path_1.sep)}.${registryAccess.getTypeByName(folderType).suffix ?? ''}${constants_1.META_XML_SUFFIX}`));
};
const getDecomposedChildType = ({ fullName, type }, packageDir) => {
const topLevelType = registryAccess.findType((t) => (0, ts_types_1.isPlainObject)(t.children) && Object.keys(t.children.types).includes(type.id));
if (!topLevelType) {
throw messages.createError('noParent', [fullName, type.name]);
}
const topLevelTypeDir = packageDir ? (0, node_path_1.join)(packageDir, topLevelType.directoryName) : topLevelType.directoryName;
return [
// parent
(0, node_path_1.join)(topLevelTypeDir, `${fullName.split('.')[0]}${node_path_1.sep}${fullName.split('.')[0]}.${topLevelType.suffix ?? ''}${constants_1.META_XML_SUFFIX}`),
// child
(0, node_path_1.join)(topLevelTypeDir, fullName.split('.')[0], type.directoryName, `${fullName.split('.')[1]}.${type.suffix ?? ''}${constants_1.META_XML_SUFFIX}`),
];
};
//# sourceMappingURL=filePathGenerator.js.map
;