@dxatscale/sfprofiles
Version:
Salesforce Profile management
350 lines (324 loc) • 16.4 kB
text/typescript
import * as path from 'path';
import { MetadataInfo, METADATA_INFO, MetadataDescribe, SOURCE_EXTENSION_REGEX } from './metadataInfo';
import FileUtils from 'utils/fileutils';
import _ from 'lodash';
import ignore from 'ignore';
import * as fs from 'fs-extra';
import * as glob from 'glob';
import { Sfpowerkit } from 'utils/sfpowerkit';
import SFPLogger, {LoggerLevel } from '@dxatscale/sfp-logger';
const SEP = /\/|\\/;
export default class MetadataFiles {
public static sourceOnly = false;
forceignore: any;
public constructor() {
if (fs.existsSync('.forceignore')) {
this.forceignore = ignore().add(fs.readFileSync('.forceignore', 'utf8').toString());
} else {
this.forceignore = ignore();
}
}
static getFullApiName(fileName: string): string {
let fullName = '';
let metadateType = MetadataInfo.getMetadataName(fileName);
let splitFilepath = fileName.split(SEP);
let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(metadateType);
if (isObjectChild) {
let objectName = splitFilepath[splitFilepath.length - 3];
let fieldName = splitFilepath[splitFilepath.length - 1].split('.')[0];
fullName = objectName.concat('.' + fieldName);
} else {
fullName = splitFilepath[splitFilepath.length - 1].split('.')[0];
}
return fullName;
}
static getFullApiNameWithExtension(fileName: string): string {
let fullName = '';
let metadateType = MetadataInfo.getMetadataName(fileName);
let splitFilepath = fileName.split(SEP);
let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(metadateType);
if (isObjectChild) {
let objectName = splitFilepath[splitFilepath.length - 3];
let fieldName = splitFilepath[splitFilepath.length - 1];
fullName = objectName.concat('.' + fieldName);
} else {
fullName = splitFilepath[splitFilepath.length - 1];
}
return fullName;
}
public static isCustomMetadata(filepath: string, name: string): boolean {
let result = true;
let splitFilepath = filepath.split(SEP);
let componentName = splitFilepath[splitFilepath.length - 1];
componentName = componentName.substring(0, componentName.indexOf('.'));
if (name === METADATA_INFO.CustomField.xmlName || name === METADATA_INFO.CustomObject.xmlName) {
//Custom Field or Custom Object
result = componentName.endsWith('__c') || componentName.endsWith('__mdt');
}
return result;
}
public static getMemberNameFromFilepath(filepath: string, name: string): string {
let member: string;
let splitFilepath = filepath.split(SEP);
let lastIndex = splitFilepath.length - 1;
let isObjectChild = METADATA_INFO.CustomObject.childXmlNames.includes(name);
let metadataDescribe: MetadataDescribe = METADATA_INFO[name];
if (isObjectChild) {
let objectName = splitFilepath[lastIndex - 2];
let fieldName = splitFilepath[lastIndex].split('.')[0];
member = objectName.concat('.' + fieldName);
} else if (metadataDescribe.inFolder) {
let baseName = metadataDescribe.directoryName;
let baseIndex = filepath.indexOf(baseName) + baseName.length;
let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator
cmpPath = cmpPath.substring(0, cmpPath.indexOf('.'));
member = cmpPath.replace(SEP, '/');
} else {
if (SOURCE_EXTENSION_REGEX.test(splitFilepath[lastIndex])) {
member = splitFilepath[lastIndex].replace(SOURCE_EXTENSION_REGEX, '');
} else {
const auraRegExp = new RegExp('aura');
const lwcRegExp = new RegExp('lwc');
const staticResourceRegExp = new RegExp('staticresources');
const experienceBundleRegExp = new RegExp('experiences');
if (auraRegExp.test(filepath) || lwcRegExp.test(filepath)) {
member = splitFilepath[lastIndex - 1];
} else if (staticResourceRegExp.test(filepath)) {
//Return the fileName
let baseName = 'staticresources';
let baseIndex = filepath.indexOf(baseName) + baseName.length;
let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator
member = cmpPath.split(SEP)[0];
let extension = path.parse(member).ext;
member = member.replace(new RegExp(extension + '$'), '');
} else if (experienceBundleRegExp.test(filepath)) {
//Return the fileName
let baseName = 'experiences';
let baseIndex = filepath.indexOf(baseName) + baseName.length;
let cmpPath = filepath.substring(baseIndex + 1); // add 1 to remove the path seperator
member = cmpPath.split(SEP)[0];
let extension = path.parse(member).ext;
member = member.replace(new RegExp(extension + '$'), '');
} else {
let extension = path.parse(splitFilepath[lastIndex]).ext;
member = splitFilepath[lastIndex].replace(new RegExp(extension + '$'), '');
}
}
}
return member;
}
public loadComponents(srcFolder: string, checkIgnore = true): void {
let metadataFiles: string[] = FileUtils.getAllFilesSync(srcFolder);
let keys = Object.keys(METADATA_INFO);
if (Array.isArray(metadataFiles) && metadataFiles.length > 0) {
metadataFiles.forEach((metadataFile) => {
let found = false;
for (let i = 0; i < keys.length; i++) {
let match = false;
if (metadataFile.endsWith(METADATA_INFO[keys[i]].sourceExtension)) {
match = true;
} else if (
METADATA_INFO[keys[i]].inFolder &&
metadataFile.endsWith(METADATA_INFO[keys[i]].folderExtension)
) {
match = true;
}
if (match) {
if (_.isNil(METADATA_INFO[keys[i]].files)) {
METADATA_INFO[keys[i]].files = [];
METADATA_INFO[keys[i]].components = [];
}
if (!checkIgnore || (checkIgnore && this.accepts(metadataFile))) {
METADATA_INFO[keys[i]].files.push(metadataFile);
let name = FileUtils.getFileNameWithoutExtension(
metadataFile,
METADATA_INFO[keys[i]].sourceExtension
);
if (METADATA_INFO[keys[i]].isChildComponent) {
let fileParts = metadataFile.split(SEP);
let parentName = fileParts[fileParts.length - 3];
if (keys[i] === 'CustomField' && parentName === 'Activity') {
//Add Activity fiels on Task and Event for reconcile
METADATA_INFO[keys[i]].components.push('Task.' + name);
METADATA_INFO[keys[i]].components.push('Event.' + name);
}
name = parentName + '.' + name;
}
METADATA_INFO[keys[i]].components.push(name);
}
found = true;
break;
}
}
if (!found) {
const auraRegExp = new RegExp('aura');
if (auraRegExp.test(metadataFile) && SOURCE_EXTENSION_REGEX.test(metadataFile)) {
if (_.isNil(METADATA_INFO.AuraDefinitionBundle.files)) {
METADATA_INFO.AuraDefinitionBundle.files = [];
METADATA_INFO.AuraDefinitionBundle.components = [];
}
if (!checkIgnore || (checkIgnore && this.accepts(metadataFile))) {
METADATA_INFO.AuraDefinitionBundle.files.push(metadataFile);
let name = FileUtils.getFileNameWithoutExtension(metadataFile);
METADATA_INFO.AuraDefinitionBundle.components.push(name);
}
}
}
});
} else {
keys.forEach((key) => {
if (_.isNil(METADATA_INFO[key].files)) {
METADATA_INFO[key].files = [];
METADATA_INFO[key].components = [];
}
});
}
}
//Check if a component is accepted by forceignore.
public accepts(filePath: string) {
return !this.forceignore.ignores(path.relative(process.cwd(), filePath));
}
public async isInModuleFolder(filePath: string) {
const packageDirectories = await Sfpowerkit.getProjectDirectories();
if (!packageDirectories || packageDirectories.length == 0) {
return false;
}
const moduleFolder = packageDirectories.find((packageFolder) => {
let packageFolderNormalized = path.relative('', packageFolder);
packageFolderNormalized = packageFolderNormalized + path.sep;
return filePath.startsWith(packageFolderNormalized);
});
return moduleFolder !== undefined;
}
/**
* Copy a file to an outpu directory. If the filePath is a Metadata file Path,
* All the metadata requirement are also copied. For example MyApexClass.cls-meta.xml will also copy MyApexClass.cls.
* Enforcing the .forceignore to ignire file ignored in the project.
* @param filePath
* @param outputFolder
*/
public static copyFile(filePath: string, outputFolder: string) {
SFPLogger.log(`Copying file ${filePath} from file system to ${outputFolder}`, LoggerLevel.DEBUG);
const LWC_IGNORE_FILES = ['jsconfig.json', '.eslintrc.json'];
const pairStatResources = METADATA_INFO.StaticResource.directoryName;
const pairStatResourcesRegExp = new RegExp(pairStatResources);
const pairAuaraRegExp = new RegExp(METADATA_INFO.AuraDefinitionBundle.directoryName);
let copyOutputFolder = outputFolder;
if (!fs.existsSync(filePath)) {
return;
}
let exists = fs.existsSync(path.join(outputFolder, filePath));
if (exists) {
return;
}
if (filePath.startsWith('.')) {
let parts = path.parse(filePath);
if (parts.dir === '') {
fs.copyFileSync(filePath, path.join(outputFolder, filePath));
return;
}
}
let fileName = path.parse(filePath).base;
//exclude lwc ignored files
if (LWC_IGNORE_FILES.includes(fileName)) {
return;
}
let filePathParts = filePath.split(SEP);
if (fs.existsSync(outputFolder) == false) {
fs.mkdirSync(outputFolder);
}
// Create folder structure
for (let i = 0; i < filePathParts.length - 1; i++) {
let folder = filePathParts[i].replace('"', '');
outputFolder = path.join(outputFolder, folder);
if (fs.existsSync(outputFolder) == false) {
fs.mkdirSync(outputFolder);
}
}
// Copy all file with same base name
let associatedFilePattern = '';
if (SOURCE_EXTENSION_REGEX.test(filePath)) {
associatedFilePattern = filePath.replace(SOURCE_EXTENSION_REGEX, '.*');
} else {
let extension = path.parse(filePath).ext;
associatedFilePattern = filePath.replace(extension, '.*');
}
let files = glob.sync(associatedFilePattern);
for (let i = 0; i < files.length; i++) {
if (fs.lstatSync(files[i]).isDirectory() == false) {
let oneFilePath = path.join('.', files[i]);
let oneFilePathParts = oneFilePath.split(SEP);
fileName = oneFilePathParts[oneFilePathParts.length - 1];
let outputPath = path.join(outputFolder, fileName);
fs.copyFileSync(files[i], outputPath);
}
}
// Hadle ObjectTranslations
// If a file fieldTranslation is copied, make sure the ObjectTranslation File is also copied
if (filePath.endsWith('Translation-meta.xml') && filePath.indexOf('globalValueSet') < 0) {
let parentFolder = filePathParts[filePathParts.length - 2];
let objectTranslation = parentFolder + METADATA_INFO.CustomObjectTranslation.sourceExtension;
let outputPath = path.join(outputFolder, objectTranslation);
let sourceFile = filePath.replace(fileName, objectTranslation);
if (fs.existsSync(sourceFile) == true) {
fs.copyFileSync(sourceFile, outputPath);
}
}
//FOR STATIC RESOURCES - WHERE THE CORRESPONDING DIRECTORY + THE ROOT META FILE HAS TO BE INCLUDED
if (pairStatResourcesRegExp.test(filePath)) {
outputFolder = path.join('.', copyOutputFolder);
let srcFolder = '.';
let staticRecourceRoot = '';
let resourceFile = '';
for (let i = 0; i < filePathParts.length; i++) {
outputFolder = path.join(outputFolder, filePathParts[i]);
srcFolder = path.join(srcFolder, filePathParts[i]);
if (filePathParts[i] === METADATA_INFO.StaticResource.directoryName) {
let fileOrDirname = filePathParts[i + 1];
let fileOrDirnameParts = fileOrDirname.split('.');
srcFolder = path.join(srcFolder, fileOrDirnameParts[0]);
outputFolder = path.join(outputFolder, fileOrDirnameParts[0]);
resourceFile = srcFolder + METADATA_INFO.StaticResource.sourceExtension;
METADATA_INFO.StaticResource.sourceExtension;
staticRecourceRoot = outputFolder + METADATA_INFO.StaticResource.sourceExtension;
if (fs.existsSync(srcFolder)) {
if (fs.existsSync(outputFolder) == false) {
fs.mkdirSync(outputFolder);
}
}
break;
}
}
if (fs.existsSync(srcFolder)) {
FileUtils.copyRecursiveSync(srcFolder, outputFolder);
}
if (fs.existsSync(resourceFile)) {
fs.copyFileSync(resourceFile, staticRecourceRoot);
}
}
//FOR AURA components and LWC components
if (pairAuaraRegExp.test(filePath)) {
outputFolder = path.join('.', copyOutputFolder);
let srcFolder = '.';
for (let i = 0; i < filePathParts.length; i++) {
outputFolder = path.join(outputFolder, filePathParts[i]);
srcFolder = path.join(srcFolder, filePathParts[i]);
if (filePathParts[i] === 'aura' || filePathParts[i] === 'lwc') {
let fileOrDirname = filePathParts[i + 1];
let fileOrDirnameParts = fileOrDirname.split('.');
srcFolder = path.join(srcFolder, fileOrDirnameParts[0]);
outputFolder = path.join(outputFolder, fileOrDirnameParts[0]);
if (fs.existsSync(srcFolder)) {
if (fs.existsSync(outputFolder) == false) {
fs.mkdirSync(outputFolder);
}
}
break;
}
}
if (fs.existsSync(srcFolder)) {
FileUtils.copyRecursiveSync(srcFolder, outputFolder);
}
}
}
}