salesforce-alm
Version:
This package contains tools, and APIs, for an improved salesforce.com developer experience.
208 lines (206 loc) • 7.69 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.canRead = exports.deleteOrderComparator = exports.cleanEmptyDirs = exports.getNestedDirectoryPaths = exports.encodeMetadataString = exports.replaceForwardSlashes = exports.removeParentDirFromPath = exports.fileNamesWithoutExtensionsMatch = exports.getContentPathWithNonStdExtFromMetadataPath = exports.getPathToDir = exports.getGrandparentDirectoryName = exports.getParentDirectoryName = exports.getFileName = exports.removeMetadataFileExtFrom = 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 path = require("path");
const fs = require("fs-extra");
const _ = require("lodash");
const glob = require("glob");
const MetadataRegistry = require("./metadataRegistry");
/**
* Returns the given filePath without '-meta.xml'
*
* @param {string} filePath
* @returns {string}
*/
exports.removeMetadataFileExtFrom = function (filePath) {
return filePath.replace(MetadataRegistry.getMetadataFileExt(), '');
};
/**
* Returns the fileName stripped of any file extensions
*
* @param {string} filePath
* @param {TypeDefObj} typeDef
* @returns {string}
*/
exports.getFileName = function (filePath) {
const filePathWithoutMetadataExt = exports.removeMetadataFileExtFrom(filePath);
return path.basename(filePathWithoutMetadataExt, path.extname(filePathWithoutMetadataExt));
};
/**
* Returns the parent directory name
* Example: /parentDir/fileName.ext returns 'parentDir'
*
* @param {string} filePath
* @returns {string}
*/
exports.getParentDirectoryName = function (filePath) {
return path.basename(path.dirname(filePath));
};
/**
* Returns the grandparent directory name
* Example: grandparentDir/parentDir/fileName.ext returns 'grandparentDir'
*
* @param {string} filePath
* @returns {string}
*/
exports.getGrandparentDirectoryName = function (filePath) {
return path.basename(path.dirname(path.dirname(filePath)));
};
/**
* Returns the truncated path to the given directory
* Example: filePath='path/to/specialDir/containing/stuff' dirName='specialDir' returns 'path/to/specialDir'
*
* @param {string} filePath
* @param {string} dirName
* @returns {string}
*/
exports.getPathToDir = function (filePath, dirName) {
const filePathParts = filePath.split(path.sep);
const indexOfGivenDir = filePathParts.indexOf(dirName);
if (indexOfGivenDir !== -1) {
let newFilePath = path.join(...filePathParts.slice(0, indexOfGivenDir + 1));
if (filePath.startsWith(path.sep)) {
newFilePath = `${path.sep}${newFilePath}`;
}
return newFilePath;
}
return null;
};
exports.getContentPathWithNonStdExtFromMetadataPath = function (metadataFilePath) {
const fileNameWithoutExtensions = exports.getFileName(metadataFilePath);
let matchingWorkspaceFiles = glob.sync(path.join(path.dirname(metadataFilePath), `${fileNameWithoutExtensions}*`));
matchingWorkspaceFiles = matchingWorkspaceFiles.map((filePath) => path.resolve(filePath)); // glob returns paths using the forward slash only, which breaks tests in Windows
return matchingWorkspaceFiles.find((docFile) => exports.fileNamesWithoutExtensionsMatch(path.basename(docFile), fileNameWithoutExtensions) &&
!docFile.endsWith(MetadataRegistry.getMetadataFileExt()));
};
exports.fileNamesWithoutExtensionsMatch = function (filename1, filename2) {
return path.basename(filename1, path.extname(filename1)) === path.basename(filename2, path.extname(filename2));
};
exports.removeParentDirFromPath = function (filePath) {
const fileName = path.basename(filePath);
return path.join(path.dirname(path.dirname(filePath)), fileName);
};
/**
* Replace forward slashes with path separators
*
* @param {string} str
* @returns {string}
*/
exports.replaceForwardSlashes = function (str) {
return str.replace(/\//g, path.sep);
};
/**
* Encode a string similarly to how Metadata API encodes (spaces not encoded)
*
* @param {string} str
* @returns {string}
*/
exports.encodeMetadataString = function (str) {
return encodeURIComponent(str).replace(/%20/g, ' ');
};
/**
* gets a list of sub-directories from a parent directory.
*
* @param {string} filePath The path to look for nested directories
* @returns {string[]} An array of all sub directories
*/
exports.getNestedDirectoryPaths = function (filePath) {
const accum = [];
const _recur = (_filePath) => {
if (_filePath) {
try {
// Is the sub folder accessible?
fs.accessSync(_filePath, fs.constants.R_OK);
const stats = fs.statSync(_filePath);
// Is it a directory?
if (stats.isDirectory()) {
accum.push(_filePath);
const dirListing = fs.readdirSync(_filePath);
// Is the folder empty?
dirListing.forEach((_path) => {
const subPath = path.join(_filePath, _path);
_recur(subPath);
});
}
}
catch (e) {
// If we can't access the filepath then lets stop.
}
}
};
_recur(filePath);
return accum;
};
/**
* Removes all the empty sub-directories
*
* @param {string} filePath The parent path to look for empty directories. This directory will also be removed if it
* ends up being empty.
*/
function cleanEmptyDirs(filePath) {
const paths = exports.getNestedDirectoryPaths(filePath);
// Sort all directory paths based on level count in descending order.
paths.sort((a, b) => {
const aLevelCount = _.split(a, path.sep).length;
const bLevelCount = _.split(b, path.sep).length;
if (aLevelCount > bLevelCount) {
return -1;
}
return aLevelCount < bLevelCount ? 1 : 0;
});
// Iterate and deleted all the empty folders. If a parent becomes empty because it only contained empty folders it
// will be deleted later because its level is one less.
paths.forEach((_path) => {
const dirListing = fs.readdirSync(_path);
if (dirListing.length === 0) {
fs.removeSync(_path);
}
});
}
exports.cleanEmptyDirs = cleanEmptyDirs;
/**
* Comparator function to sort an array of strings by parent child relationship.
*
* @param left {string} The left path string
* @param right {string} The right path string
* @returns
* if the right path starts with the left path return 1
* if the left path starts with the right path return -1
* Everything else returns the result of left.localeCompare(right)
*/
function deleteOrderComparator(left, right) {
if (left.startsWith(right) && left.length > right.length) {
return -1;
}
if (right.startsWith(left) && right.length > left.length) {
return 1;
}
return left.localeCompare(right);
}
exports.deleteOrderComparator = deleteOrderComparator;
/**
* A better file exists function that ensures a file can be read from the filesystem.
*
* @param path The absolute files path or relative path to a file from the CWD.
*/
// eslint-disable-next-line @typescript-eslint/no-shadow
function canRead(path) {
if (path) {
try {
fs.access(path, fs.constants.R_OK);
return true;
}
catch (e) {
return false;
}
}
return false;
}
exports.canRead = canRead;
//# sourceMappingURL=sourcePathUtil.js.map