import * as path from "path" ; import { MetadataInfo , METADATA_INFO , MetadataDescribe , SOURCE_EXTENSION_REGEX } from "./MetadataInfo" ; import FileUtils from "../utils/Fileutils" ; import * as _ from "lodash" ; import ignore from "ignore" ; import * as fs from "fs-extra" ; import * as glob from "glob" ; import ProjectConfig from "../project/ProjectConfig" ; const SEP = /\/|\\/ ; export default class MetadataFiles { public static sourceOnly : boolean = 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 ) { 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 ) { 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 , name ) { 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 ) { result = componentName . endsWith ( "__c" ) || componentName . endsWith ( "__mdt" ) ; } return result ; } public static getMemberNameFromFilepath ( filepath , name ) { let member ; let splitFilepath = filepath . split ( SEP ) ; let lastIndex = splitFilepath . length - 1 ; let isObjectChild = METADATA_INFO . CustomObject . childXmlNames . includes ( name ) ; let 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 ) ; 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 ) ) { let baseName = "staticresources" ; let baseIndex = filepath . indexOf ( baseName ) + baseName . length ; let cmpPath = filepath . substring ( baseIndex + 1 ) ; member = cmpPath . split ( SEP ) [ 0 ] ; let extension = path . parse ( member ) . ext ; member = member . replace ( new RegExp ( extension + "$" ) , "" ) ; } else if ( experienceBundleRegExp . test ( filepath ) ) { let baseName = "experiences" ; let baseIndex = filepath . indexOf ( baseName ) + baseName . length ; let cmpPath = filepath . substring ( baseIndex + 1 ) ; 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 , checkIgnore = true ) { var metadataFiles = 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 ] ; 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 = [ ] ; } } ) ; } } public accepts ( filePath ) { return ! this . forceignore . ignores ( path . relative ( process . cwd ( ) , filePath ) ) ; } public async isInModuleFolder ( filePath ) { const packageDirectories = ProjectConfig . getSFDXPackageManifest ( null ) . packageDirectories . map ( ( elem ) => elem . path ) ; if ( ! packageDirectories || packageDirectories . length == 0 ) { return false ; } const moduleFolder = packageDirectories . find ( packageFolder => { let packageFolderNormalized = path . relative ( "" , packageFolder ) ; return filePath . startsWith ( packageFolderNormalized ) ; } ) ; return moduleFolder !== undefined ; } param filePath param outputFolder public static copyFile ( filePath , outputFolder ) { console . log ( `Copying file ${ filePath } from file system to ${ outputFolder }` ) ; 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 ; if ( LWC_IGNORE_FILES . includes ( fileName ) ) { return ; } let filePathParts = filePath . split ( SEP ) ; if ( fs . existsSync ( outputFolder ) == false ) { fs . mkdirSync ( outputFolder ) ; } 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 ) ; } } 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 ) ; } } 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 ) ; } } 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 ) ; } } 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 ) ; } } } }