@salesforce/source-deploy-retrieve
Version:
JavaScript library to run Salesforce metadata deploys and retrieves
146 lines • 6.29 kB
JavaScript
/*
* 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
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.fnJoin = exports.calculateRelativePath = void 0;
exports.baseName = baseName;
exports.baseWithoutSuffixes = baseWithoutSuffixes;
exports.extName = extName;
exports.parentName = parentName;
exports.trimUntil = trimUntil;
exports.parseMetadataXml = parseMetadataXml;
exports.parseNestedFullName = parseNestedFullName;
const node_path_1 = require("node:path");
const constants_1 = require("../common/constants");
/**
* Get the file or directory name at the end of a path. Different from `path.basename`
* in that it strips anything after the first '.' in the name.
*
* @param fsPath The path to evaluate
*/
function baseName(fsPath) {
return (0, node_path_1.basename)(fsPath).split('.')[0];
}
/**
* the above baseName function doesn't handle components whose names have a `.` in them.
* this will handle that, but requires you to specify the mdType to check suffixes for.
*
* @param fsPath The path to evaluate
*/
function baseWithoutSuffixes(fsPath, mdType) {
return (0, node_path_1.basename)(fsPath).replace(constants_1.META_XML_SUFFIX, '').split('.').filter(stringIsNotSuffix(mdType)).join('.');
}
const stringIsNotSuffix = (type) => (part) => part !== type.suffix && (!type.legacySuffix || part !== type.legacySuffix);
/**
* Get the name of file path extension. Different from path.extname in that it
* does not include the '.' in the extension name. Returns an empty string if
* there is no extension.
*
* @param fsPath The path to evaluate
*/
function extName(fsPath) {
const split = (0, node_path_1.extname)(fsPath).split('.');
return split.length > 1 ? split[1] : split[0];
}
/**
* Get the name of the parent to the last portion of a path
*
* @param fsPath The path to evaluate
*/
function parentName(fsPath) {
return (0, node_path_1.basename)((0, node_path_1.dirname)(fsPath));
}
/**
* Trim a path up until and including the given part. Returns `fsPath`
* if the path `part` was not found.
*
* @param fsPath Path to trim
* @param part Path part to trim up until
* @param untilLast Trim until the *last* occurrence of `part`
*/
function trimUntil(fsPath, part, untilLast = false) {
const parts = fsPath.split(node_path_1.sep);
const partIndex = untilLast ? parts.lastIndexOf(part) : parts.findIndex((p) => part === p);
if (partIndex === -1) {
return fsPath;
}
return parts.slice(partIndex).join(node_path_1.sep);
}
/**
* Returns the `MetadataXml` info from a given file path. If the path is not a
* metadata xml file (-meta.xml), returns `undefined`.
*
* @param fsPath - File path to parse
* @returns MetadataXml info or undefined
*/
function parseMetadataXml(fsPath) {
const match = new RegExp(/(.+)\.(.+)-meta\.xml/).exec((0, node_path_1.basename)(fsPath));
if (match) {
return { fullName: match[1], suffix: match[2], path: fsPath };
}
}
/**
* Returns the fullName for a nested metadata source file. This is for metadata
* types that can be nested more than 1 level such as report and reportFolder,
* dashboard and dashboardFolder, etc. It uses the directory name for the metadata type
* as the starting point (non-inclusively) to parse the fullName.
*
* Examples:
* (source format path)
* fsPath: force-app/main/default/reports/foo/bar/My_Report.report-meta.xml
* returns: foo/bar/My_Report
*
* (mdapi format path)
* fsPath: unpackaged/reports/foo/bar-meta.xml
* returns: foo/bar
*
* @param fsPath - File path to parse
* @param directoryName - name of directory to use as a parsing index
* @returns the FullName
*/
function parseNestedFullName(fsPath, directoryName) {
const pathSplits = fsPath.split(node_path_1.sep);
// Exit if the directoryName is not included in the file path.
if (!pathSplits.includes(directoryName)) {
return;
}
const pathPrefix = pathSplits.slice(pathSplits.lastIndexOf(directoryName) + 1);
// the eslint comment should remain until strictMode is fully implemented
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
const fileName = pathSplits.pop().replace('-meta.xml', '').split('.')[0];
pathPrefix[pathPrefix.length - 1] = fileName;
return pathPrefix.join('/');
}
const calculateRelativePath = (format) => (types) => (fullName) => (fsPath) => {
const base = format === 'source' ? constants_1.DEFAULT_PACKAGE_ROOT_SFDX : '';
const { directoryName, suffix, inFolder, folderType, folderContentType } = types.self;
// if there isn't a suffix, assume this is a mixed content component that must
// reside in the directoryName of its type. trimUntil maintains the folder structure
// the file resides in for the new destination. This also applies to inFolder types:
// (report, dashboard, emailTemplate, document) and their folder container types:
// (reportFolder, dashboardFolder, emailFolder, documentFolder)
// It also applies to DigitalExperienceBundle types as we need to maintain the folder structure
if (!suffix ||
Boolean(inFolder) ||
typeof folderContentType === 'string' ||
['digitalexperiencebundle', 'digitalexperience'].includes(types.self.id)) {
return (0, node_path_1.join)(base, trimUntil(fsPath, directoryName, true));
}
if (folderType) {
// types like Territory2Model have child types inside them. We have to preserve those folder structures
if (types.parentType?.folderType && types.parentType?.folderType !== types.self.id) {
return (0, node_path_1.join)(base, trimUntil(fsPath, types.parentType.directoryName));
}
return (0, node_path_1.join)(base, directoryName, fullName.split('/')[0], (0, node_path_1.basename)(fsPath));
}
return (0, node_path_1.join)(base, directoryName, (0, node_path_1.basename)(fsPath));
};
exports.calculateRelativePath = calculateRelativePath;
/** (a)(b)=> a/b */
const fnJoin = (a) => (b) => (0, node_path_1.join)(a, b);
exports.fnJoin = fnJoin;
//# sourceMappingURL=path.js.map
;