qforce
Version:
Commands to help with salesforce development.
193 lines (192 loc) • 10.5 kB
JavaScript
;
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 }
];