@salesforce/packaging
Version:
Packaging library for the Salesforce packaging platform
126 lines • 7.06 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PackageProfileApi = void 0;
/*
* Copyright 2026, 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.
*/
const node_path_1 = __importDefault(require("node:path"));
const node_os_1 = __importDefault(require("node:os"));
const node_fs_1 = __importDefault(require("node:fs"));
const globby_1 = __importDefault(require("globby"));
const core_1 = require("@salesforce/core");
const kit_1 = require("@salesforce/kit");
const profileRewriter_1 = require("./profileRewriter");
core_1.Messages.importMessagesDirectory(__dirname);
const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'profile_api');
/*
* This class provides functions used to re-write .profiles in the project package directories when creating a package2 version.
* All profiles found in the project package directories are extracted out and then re-written to only include metadata in the
* profile that is relevant to the source in the package directory being packaged.
*/
class PackageProfileApi extends kit_1.AsyncCreatable {
project;
includeUserLicenses = false;
constructor(options) {
super(options);
this.project = options.project;
this.includeUserLicenses = options.includeUserLicenses ?? false;
}
// eslint-disable-next-line class-methods-use-this,@typescript-eslint/no-empty-function
async init() { }
/**
* For any profile present in the project package directories, this function generates a subset of data that only
* contains references to items in the manifest.
*
* return a list of profile file locations that need to be removed from the package because they are empty
*
* @param destPath location of new profiles
* @param manifestTypes: array of objects { name: string, members: string[] } that represent package xml types
* @param excludedDirectories Directories to not include profiles from
*/
generateProfiles(destPath, manifestTypes, excludedDirectories = []) {
const logger = core_1.Logger.childFromRoot('PackageProfileApi');
return (this.getProfilesWithNamesAndPaths(excludedDirectories)
.map(({ profilePath, name: profileName }) => {
const originalProfile = (0, profileRewriter_1.profileStringToProfile)(node_fs_1.default.readFileSync(profilePath, 'utf-8'));
const adjustedProfile = (0, profileRewriter_1.profileRewriter)(originalProfile, (0, profileRewriter_1.manifestTypesToMap)(manifestTypes), this.includeUserLicenses);
return {
profileName,
profilePath,
hasContent: Object.keys(adjustedProfile).length,
adjustedProfile,
removedSettings: getRemovedSettings(originalProfile, adjustedProfile),
xmlFileLocation: getXmlFileLocation(destPath, profilePath),
};
})
// side effect: modify profiles in place
.filter(({ hasContent, profileName, removedSettings, profilePath, xmlFileLocation, adjustedProfile }) => {
if (!hasContent) {
logger.warn(`Profile ${profileName} has no content after filtering. It will still be part of the package but you can remove it if it's not needed.`);
return true;
}
else {
logger.info(profileApiMessages.getMessage('addProfileToPackage', [profileName, profilePath]));
removedSettings.forEach((setting) => {
logger.info(profileApiMessages.getMessage('removeProfileSetting', [setting, profileName]));
});
node_fs_1.default.writeFileSync(xmlFileLocation, (0, profileRewriter_1.profileObjectToString)(adjustedProfile), 'utf-8');
}
})
.map(({ xmlFileLocation }) => xmlFileLocation.replace(/(.*)(\.profile)/, '$1')));
}
/**
* Filter out all profiles in the manifest and if any profiles exist in the project package directories, add them to the manifest.
*
* @param typesArr array of objects { name[], members[] } that represent package types JSON.
* @param excludedDirectories Direcotires not to generate profiles for
*/
filterAndGenerateProfilesForManifest(typesArr, excludedDirectories = []) {
const profilePathsWithNames = this.getProfilesWithNamesAndPaths(excludedDirectories);
// Filter all profiles, and add back the ones we found names for
return typesArr
.filter((kvp) => kvp.name !== 'Profile')
.concat([{ name: 'Profile', members: profilePathsWithNames.map((i) => i.name) }]);
}
// Look for profiles in all package directories
findAllProfiles(excludedDirectories = []) {
const ignore = excludedDirectories.map((dir) => `**/${dir.split(node_path_1.default.sep).join(node_path_1.default.posix.sep)}/**`);
const patterns = this.project
.getUniquePackageDirectories()
.map((pDir) => pDir.fullPath)
.map((fullDir) => node_os_1.default.type() === 'Windows_NT'
? node_path_1.default.posix.join(...fullDir.split(node_path_1.default.sep), '**', '*.profile-meta.xml')
: node_path_1.default.join(fullDir, '**', '*.profile-meta.xml'));
return globby_1.default.sync(patterns, { ignore });
}
getProfilesWithNamesAndPaths(excludedDirectories) {
return this.findAllProfiles(excludedDirectories)
.map((profilePath) => ({ profilePath, name: profilePathToName(profilePath) }))
.filter(isProfilePathWithName);
}
}
exports.PackageProfileApi = PackageProfileApi;
const isProfilePathWithName = (profilePathWithName) => typeof profilePathWithName.name === 'string';
const profilePathToName = (profilePath) => profilePath.match(/([^/]+)\.profile-meta.xml/)?.[1];
const getXmlFileLocation = (destPath, profilePath) => node_path_1.default.join(destPath, node_path_1.default.basename(profilePath).replace(/(.*)(-meta.xml)/, '$1'));
const getRemovedSettings = (originalProfile, adjustedProfile) => {
const originalProfileSettings = Object.keys(originalProfile);
const adjustedProfileSettings = new Set(Object.keys(adjustedProfile));
return originalProfileSettings.filter((setting) => !adjustedProfileSettings.has(setting));
};
//# sourceMappingURL=packageProfileApi.js.map