UNPKG

qforce

Version:

Commands to help with salesforce development.

193 lines (192 loc) 10.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const command_1 = require("@oclif/command"); const cli_ux_1 = require("cli-ux"); const utility_1 = require("../../helper/utility"); const metaUtil = require('../../helper/metadataUtil'); const path = require('path'); const fs = require('fs'); const execa = require('execa'); const YAML = require('yaml'); const sfdx = require('sfdx-node'); const xmljs = require('xml-js'); const _ = require('lodash'); class DevFeature extends command_1.Command { async run() { const { args, flags } = this.parse(DevFeature); cli_ux_1.default.action.start('started processing feature ' + args.featureName); let settings, sfdxConfig; if (fs.existsSync(utility_1.getAbsolutePath('.qforce/settings.json'))) { settings = JSON.parse(fs.readFileSync(utility_1.getAbsolutePath('.qforce/settings.json'))); } if (fs.existsSync(path.join(process.cwd(), '.sfdx', 'sfdx-config.json'))) { sfdxConfig = JSON.parse(fs.readFileSync(path.join(process.cwd(), '.sfdx', 'sfdx-config.json'))); } const metadataMap = metaUtil.metadataMap; const metadataRegex = metaUtil.metadataRegex; const targetusername = flags.username || settings.targetusername || sfdxConfig.defaultusername; const featureYamlPath = settings.featureYamlPath || '.qforce/features'; const featureMetaPath = settings.featureMetaPath || '.qforce/features'; const packageBasePath = settings.packageBasePath || 'force-app/main/default'; const buildFromDirPath = flags.path || packageBasePath; let featureName = args.featureName.replace('/', '-'); let featureYAML; let yamlPath = `${featureYamlPath}/${featureName}/${featureName}.yml`; if (flags.start) { if (!fs.existsSync(path.dirname(yamlPath))) { fs.mkdirSync(path.dirname(yamlPath), { recursive: true }); } fs.writeFileSync(utility_1.getAbsolutePath(yamlPath), YAML.stringify({ ManualSteps: [{ ExampleManualStep: [] }] }), { encoding: 'utf-8' }); let command = `code ${yamlPath}`; execa.commandSync(command); } if (!fs.existsSync(utility_1.getAbsolutePath(yamlPath))) { cli_ux_1.default.action.stop('File not found. Check file path. Remember to start a feature first.'); } if (flags.buildFromDiff || flags.buildFromDir) { if (flags.buildFromDiff && (!args.commit1 || !args.commit2)) { cli_ux_1.default.action.stop('Provide commits to calculate diff from.'); } featureYAML = YAML.parse(fs.readFileSync(yamlPath, 'utf-8')); let filePaths; if (flags.buildFromDiff) { const diffFiles = await execa('git', ['diff', '--name-only', args.commit1, args.commit2]); filePaths = diffFiles.stdout.split('\n'); } else if (flags.buildFromDir) { filePaths = await utility_1.getFiles(buildFromDirPath); filePaths = filePaths.map(absolutePath => path.relative('', absolutePath)); } for (let filePath of filePaths) { if (flags.buildFromDiff && filePath.indexOf(packageBasePath) == -1) continue; if (!fs.existsSync(filePath)) continue; const filePathParts = filePath.replace(packageBasePath + '/', '').split('/'); let metadatType = metadataMap.get(filePathParts[0]) || filePathParts[0]; let metadatName = filePathParts[1]; // apply regex when available if (metadataRegex.get(filePathParts[0])) { metadatName = filePathParts[1].replace(metadataRegex.get(filePathParts[0]), ''); } if (metadatType == 'CustomLabels') continue; if (metadatType == 'CustomObject' && filePathParts.length > 2) { if (filePathParts[2] == 'fields') { let compName = filePathParts[3].replace(/\.field-meta\.xml$/i, ''); if (!featureYAML.CustomField) featureYAML.CustomField = []; featureYAML.CustomField.push(metadatName + '.' + compName); } if (filePathParts[2] == 'recordTypes') { let compName = filePathParts[3].replace(/\.recordType-meta\.xml$/i, ''); if (!featureYAML.RecordType) featureYAML.RecordType = []; featureYAML.RecordType.push(metadatName + '.' + compName); } if (filePathParts[2] == 'compactLayouts') { let compName = filePathParts[3].replace(/\.compactLayout-meta\.xml$/i, ''); if (!featureYAML.CompactLayout) featureYAML.CompactLayout = []; featureYAML.CompactLayout.push(metadatName + '.' + compName); } if (filePathParts[2] == 'listViews') { let compName = filePathParts[3].replace(/\.listView-meta\.xml$/i, ''); if (!featureYAML.ListView) featureYAML.ListView = []; featureYAML.ListView.push(metadatName + '.' + compName); } if (filePathParts[2] == 'webLinks') { let compName = filePathParts[3].replace(/\.webLink-meta\.xml$/i, ''); if (!featureYAML.WebLink) featureYAML.WebLink = []; featureYAML.WebLink.push(metadatName + '.' + compName); } } if (metadatType == 'DocumentFolder' && filePathParts.length > 2) { let documentName = filePathParts[2].replace(/\..*$/i, ''); if (!featureYAML.Document) featureYAML.Document = []; featureYAML.Document.push(metadatName + '/' + documentName); } if (metadatType == 'EmailFolder' && filePathParts.length > 2) { let emailTemplate = filePathParts[2].replace(/\..*$/i, ''); if (!featureYAML.EmailTemplate) featureYAML.EmailTemplate = []; featureYAML.EmailTemplate.push(metadatName + '/' + emailTemplate); } //let metadatName = filePathParts[1].replace(/\..*\.xml$/i, '').replace(/\.(cls|page|asset|trigger)$/i, '') if (!featureYAML[metadatType]) featureYAML[metadatType] = []; featureYAML[metadatType].push(metadatName); } for (let key in featureYAML) { featureYAML[key] = _.uniqWith(featureYAML[key], _.isEqual); } fs.writeFileSync(utility_1.getAbsolutePath(yamlPath), YAML.stringify(featureYAML), { encoding: 'utf-8' }); } this.log('Creating xml package file for ' + args.featureName); featureYAML = YAML.parse(fs.readFileSync(yamlPath, 'utf-8')); let featureXML = utility_1.yaml2xml(featureYAML, '50.0'); let xmlOptions = { spaces: 4, compact: false, declerationKey: 'decleration', attributesKey: 'attributes' }; fs.writeFileSync(utility_1.getAbsolutePath(yamlPath.replace(/yml$/i, 'xml')), xmljs.js2xml(featureXML, xmlOptions), { encoding: 'utf-8' }); const retrievePathBase = `${featureMetaPath}/${featureName}/metadata`; if (flags.retrieve) { cli_ux_1.default.action.start('Retrieving package for ' + args.featureName); await sfdx.source.retrieve({ manifest: yamlPath.replace(/yml$/i, 'xml'), targetusername: targetusername, _quiet: false, _rejectOnError: true }).then((result) => { this.log(result); for (let file of result.inboundFiles) { let retrievePath = `${retrievePathBase}/${file.filePath}`; if (!fs.existsSync(path.dirname(retrievePath))) { fs.mkdirSync(path.dirname(retrievePath), { recursive: true }); } fs.copyFileSync(file.filePath, retrievePath); this.log('Retrieved ' + file.filePath); } }).catch((error) => { this.log(error); }); } if (flags.deploy) { sfdx.source.deploy({ targetusername: targetusername, manifest: yamlPath.replace(/yml$/i, 'xml'), json: true, _rejectOnError: true }) .then((result) => { cli_ux_1.default.action.stop(JSON.stringify(result, null, 4)); }).catch((error) => { cli_ux_1.default.action.stop(error[0].message); }); } } } exports.default = DevFeature; DevFeature.description = 'To retrieve and deploy source based on YAML file.'; DevFeature.aliases = ['feature', 'dev:feature']; DevFeature.flags = { help: command_1.flags.help({ char: 'h' }), start: command_1.flags.boolean({ char: 's', description: 'Start a new feature. Will create YAML file and folder if not already exist.' }), buildFromDiff: command_1.flags.boolean({ description: 'Build metadata components by running a diff.' }), buildFromDir: command_1.flags.boolean({ description: 'Build metadata components based on directory contents.' }), toXml: command_1.flags.boolean({ description: 'Convert yml file to xml.' }), toYaml: command_1.flags.boolean({ description: 'Convert xml file to yml.' }), path: command_1.flags.string({ char: 'p', description: 'Path to app directory.' }), retrieve: command_1.flags.boolean({ char: 'r', description: 'Retrieve source based on YAML configuration.' }), deploy: command_1.flags.boolean({ char: 'd', description: 'Deploys source already retrieved.' }), username: command_1.flags.string({ char: 'u' }), }; DevFeature.args = [ { name: 'featureName', required: true }, { name: 'commit1', required: false }, { name: 'commit2', required: false } ];