UNPKG

sfdx-hardis

Version:

Swiss-army-knife Toolbox for Salesforce. Allows you to define a complete CD/CD Pipeline. Orchestrate base commands and assist users with interactive wizards

142 lines (140 loc) 6.94 kB
/* jscpd:ignore-start */ import { SfCommand, Flags, requiredOrgFlagWithDeprecations } from '@salesforce/sf-plugins-core'; import { Messages, SfError } from '@salesforce/core'; import c from 'chalk'; import fs from 'fs-extra'; import ora from 'ora'; import * as path from 'path'; import { execCommand, uxLog } from '../../../common/utils/index.js'; import { prompts } from '../../../common/utils/prompts.js'; import { MetadataUtils } from '../../../common/metadata-utils/index.js'; import { glob } from 'glob'; import { GLOB_IGNORE_PATTERNS } from '../../../common/utils/projectUtils.js'; import { applyAllReplacementsDefinitions } from '../../../common/utils/xmlUtils.js'; Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); const messages = Messages.loadMessages('sfdx-hardis', 'org'); export default class PurgeRef extends SfCommand { static title = 'Purge References'; static description = `Purge references to any string in org metadatas before a deployment. For example, this can be handy if you need to change the type of a custom field from Master Detail to Lookup. USE WITH EXTREME CAUTION AND CAREFULLY READ THE MESSAGES !`; static examples = ['$ sf hardis:misc:purge-references']; static flags = { references: Flags.string({ char: 'r', description: 'Comma-separated list of references to find in metadatas', }), debug: Flags.boolean({ char: 'd', default: false, description: messages.getMessage('debugMode'), }), websocket: Flags.string({ description: messages.getMessage('websocket'), }), skipauth: Flags.boolean({ description: 'Skip authentication check when a default username is required', }), 'target-org': requiredOrgFlagWithDeprecations, }; // Set this to true if your command requires a project workspace; 'requiresProject' is false by default static requiresProject = true; /* jscpd:ignore-end */ ignorePatterns = GLOB_IGNORE_PATTERNS; referenceStrings = []; referenceStringsLabel; allMatchingSourceFiles = []; spinnerCustom; async run() { uxLog(this, c.yellow(c.bold(PurgeRef.description))); const { flags } = await this.parse(PurgeRef); // Collect input parameters this.referenceStrings = (flags?.references || '').split(','); if (this.referenceStrings.length == 1 && this.referenceStrings[0] === '') { const refPromptResult = await prompts({ type: 'text', message: 'Please input a comma-separated list of strings that you want to purge (example: Affaire__c)', }); this.referenceStrings = refPromptResult.value.split(','); } if (this.referenceStrings.length == 1 && this.referenceStrings[0] === '') { throw new SfError('You must input at least one string to check for references'); } for (const refString of this.referenceStrings) { if (refString.endsWith('__c') && !this.referenceStrings.includes(refString.replace('__c', '__r'))) { this.referenceStrings.push(refString.replace('__c', '__r')); } } this.referenceStringsLabel = this.referenceStrings.join(','); // Retrieve metadatas if necessary const retrieveNeedRes = await prompts({ type: 'select', message: `Are your local sources up to date with target org ${flags['target-org'].getUsername()}, or do you need to retrieve some of them ?`, choices: [ { value: true, title: 'My local sfdx sources are up to date with the target org' }, { value: false, title: 'I need to retrieve metadatas :)' }, ], }); if (retrieveNeedRes.value === false) { const metadatas = await MetadataUtils.promptMetadataTypes(); const metadataArg = metadatas.map((metadataType) => metadataType.xmlName).join(' '); await execCommand(`sf project retrieve start --ignore-conflicts --metadata ${metadataArg}`, this, { fail: true }); } // Find sources that contain references this.spinnerCustom = ora({ text: `Browsing sources to find references to ${this.referenceStringsLabel}...`, spinner: 'moon', }).start(); const packageDirectories = this.project?.getPackageDirectories() || []; this.allMatchingSourceFiles = []; for (const packageDirectory of packageDirectories) { const sourceFiles = await glob('*/**/*.{cls,trigger,xml}', { ignore: this.ignorePatterns, cwd: packageDirectory.fullPath, }); const matchingSourceFiles = sourceFiles .filter((sourceFile) => { sourceFile = path.join(packageDirectory.path, sourceFile); const fileContent = fs.readFileSync(sourceFile, 'utf8'); return this.referenceStrings.some((refString) => fileContent.includes(refString)); }) .map((sourceFile) => path.join(packageDirectory.path, sourceFile)); this.allMatchingSourceFiles.push(...matchingSourceFiles); } this.spinnerCustom.succeed(`Found ${this.allMatchingSourceFiles.length} sources with references`); this.allMatchingSourceFiles.sort(); uxLog(this, 'Matching files:\n' + c.grey(this.allMatchingSourceFiles.join('\n'))); // Handling Apex classes await applyAllReplacementsDefinitions(this.allMatchingSourceFiles, this.referenceStrings, this.getAllReplacements()); return { message: 'Command completed' }; } getAllReplacements() { return [ // Apex { extensions: ['.cls', '.trigger'], label: 'Apex', type: 'code', replaceMode: ['line'], refRegexes: [ // , REF , { regex: `,{{REF}},`, replace: ',' }, { regex: `, {{REF}},`, replace: ',' }, { regex: `,{{REF}} ,`, replace: ',' }, { regex: `, {{REF}} ,`, replace: ',' }, // , REF = xxx , { regex: `,{{REF}}[ |=].+\\,`, replace: ',' }, { regex: `, {{REF}}[ |=].+\\,`, replace: ',' }, { regex: `,{{REF}}[ |=].+\\, `, replace: ',' }, { regex: `, {{REF}}[ |=].+\\ ,`, replace: ',' }, // , REF = xxx ) { regex: `,{{REF}}[ |=].+\\)`, replace: ')' }, { regex: `, {{REF}}[ |=].+\\)`, replace: ')' }, // REF = xxx , { regex: `{{REF}}[ |=].+\\)`, replace: ')' }, ], }, ]; } } //# sourceMappingURL=purge-references.js.map