UNPKG

@salesforce/source-deploy-retrieve

Version:

JavaScript library to run Salesforce metadata deploys and retrieves

179 lines 8.25 kB
"use strict"; /* * Copyright 2025, Salesforce, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.shouldConvertPaths = exports.toKey = exports.isComponentNotFoundWarningMessage = exports.getState = exports.getDeployMessages = exports.createResponses = exports.sanitizeDeployMessage = void 0; const node_path_1 = require("node:path"); const sfError_1 = require("@salesforce/core/sfError"); const kit_1 = require("@salesforce/kit"); const registry_1 = require("../registry/registry"); const types_1 = require("./types"); const diagnosticUtil_1 = require("./diagnosticUtil"); const utils_1 = require("./utils"); /** * Fix any issues with the deploy message returned by the api. * TODO: remove cases if fixes are made in the api. */ const sanitizeDeployMessage = (message) => { if (!hasComponentType(message)) { throw new sfError_1.SfError(`Missing componentType in deploy message ${message.fullName} ${message.fileName}`); } // mdapi error messages have the type as "FooSettings" but SDR only recognizes "Settings" if (message.componentType.endsWith('Settings') && message.fileName.endsWith('.settings')) { return { ...message, componentType: 'Settings', }; } if (message.componentType === registry_1.registry.types.lightningcomponentbundle.name) { return { ...message, fullName: message.fullName.replace(/markup:\/\/[a-z|0-9|_]+:/i, ''), }; } if (message.componentType === registry_1.registry.types.document.name) { return { ...message, // strip document extension from fullName fullName: (0, node_path_1.join)((0, node_path_1.dirname)(message.fullName), (0, node_path_1.basename)(message.fullName, (0, node_path_1.extname)(message.fullName))), }; } // Treat emailTemplateFolder as EmailFolder if (message.componentType === registry_1.registry.types.emailtemplatefolder.name) { return { ...message, // strip document extension from fullName componentType: registry_1.registry.types.emailfolder.name, }; } return message; }; exports.sanitizeDeployMessage = sanitizeDeployMessage; // components with children are already taken care of through the messages, so don't walk their content directories. const shouldWalkContent = (component) => typeof component.content === 'string' && (!component.type.children || Object.values(component.type.children.types).some((t) => t.unaddressableWithoutParent === true || t.isAddressable === false)); const createResponses = (projectPath) => (component, responseMessages) => responseMessages.flatMap((message) => { const state = (0, exports.getState)(message); const base = { fullName: component.fullName, type: component.type.name }; if (state === types_1.ComponentStatus.Failed) { return [{ ...base, state, ...(0, diagnosticUtil_1.parseDeployDiagnostic)(component, message) }]; } return ((0, utils_1.isWebAppBundle)(component) ? [ { ...base, state, filePath: component.content, }, ...component.walkContent().map((filePath) => ({ fullName: getWebAppBundleContentFullName(component)(filePath), type: 'DigitalExperience', state, filePath, })), ] : [ ...(shouldWalkContent(component) ? component.walkContent().map((filePath) => ({ ...base, state, filePath })) : []), ...(component.xml ? [{ ...base, state, filePath: component.xml }] : []), ]).map((response) => ({ ...response, filePath: // deployResults will produce filePaths relative to cwd, which might not be set in all environments // if our CS had a projectDir set, we'll make the results relative to that path unless it already is projectPath && process.cwd() !== projectPath && !response.filePath.startsWith(projectPath) ? (0, node_path_1.join)(projectPath, response.filePath) : response.filePath, })); }); exports.createResponses = createResponses; const getWebAppBundleContentFullName = (component) => (filePath) => { // Normalize paths to ensure relative() works correctly on Windows const normalizedContent = component.content.split(node_path_1.sep).join(node_path_1.posix.sep); const normalizedFilePath = filePath.split(node_path_1.sep).join(node_path_1.posix.sep); const relPath = node_path_1.posix.relative(normalizedContent, normalizedFilePath); return node_path_1.posix.join(component.fullName, relPath); }; /** * Groups messages from the deploy result by component fullName and type */ const getDeployMessages = (result) => { const messageMap = new Map(); const failedComponentKeys = new Set(); const failureMessages = (0, kit_1.ensureArray)(result.details.componentFailures); const successMessages = (0, kit_1.ensureArray)(result.details.componentSuccesses); for (const failure of failureMessages) { const sanitized = (0, exports.sanitizeDeployMessage)(failure); const componentLike = { fullName: sanitized.fullName, type: sanitized.componentType, }; const key = (0, exports.toKey)(componentLike); if (!messageMap.has(key)) { messageMap.set(key, []); } messageMap.get(key)?.push(sanitized); failedComponentKeys.add(key); } for (const success of successMessages) { const sanitized = (0, exports.sanitizeDeployMessage)(success); const componentLike = { fullName: sanitized.fullName, type: sanitized.componentType, }; const key = (0, exports.toKey)(componentLike); // this will ensure successes aren't reported if there is a failure for // the same component. e.g. lwc returns failures and successes if (!failedComponentKeys.has(key)) { messageMap.set(key, [sanitized]); } } return messageMap; }; exports.getDeployMessages = getDeployMessages; const getState = (message) => { if (isTrue(message.created)) { return types_1.ComponentStatus.Created; } else if (isTrue(message.changed)) { return types_1.ComponentStatus.Changed; } else if (isTrue(message.deleted)) { return types_1.ComponentStatus.Deleted; } else if (!isTrue(message.success)) { return types_1.ComponentStatus.Failed; } return types_1.ComponentStatus.Unchanged; }; exports.getState = getState; /* Type guard for asserting that a DeployMessages has a componentType, problem, and problemType === Warning*/ const isComponentNotFoundWarningMessage = (message) => hasComponentType(message) && message.problemType === 'Warning' && typeof message.problem === 'string' && message.problem?.startsWith(`No ${message.componentType} named: `); exports.isComponentNotFoundWarningMessage = isComponentNotFoundWarningMessage; const hasComponentType = (message) => typeof message.componentType === 'string'; const toKey = (component) => { const type = typeof component.type === 'string' ? component.type : component.type.name; return `${type}#${exports.shouldConvertPaths ? component.fullName.split(node_path_1.sep).join(node_path_1.posix.sep) : component.fullName}`; }; exports.toKey = toKey; const isTrue = (value) => value === 'true' || value === true; exports.shouldConvertPaths = node_path_1.sep !== node_path_1.posix.sep; //# sourceMappingURL=deployMessages.js.map