@salesforce/packaging 2.2.9-profiles.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,6 @@ import { SaveResult } from 'jsforce';
4
4
  import { Attributes } from 'graphology-types';
5
5
  import { Optional } from '@salesforce/ts-types';
6
6
  import { ConvertResult } from '@salesforce/source-deploy-retrieve';
7
- import { Package } from 'jsforce/lib/api/metadata';
8
7
  import { PackageProfileApi } from '../package/packageProfileApi';
9
8
  import { PackageAncestryNode } from '../package/packageAncestry';
10
9
  import { PackagingSObjects } from './packagingSObjects';
@@ -49,6 +48,7 @@ export type PackageVersionCreateRequestResult = {
49
48
  CreatedDate: string;
50
49
  HasMetadataRemoved: boolean | null;
51
50
  CreatedBy: string;
51
+ ConvertedFromVersionId: string | null;
52
52
  };
53
53
  export declare const PackageVersionCreateRequestResultInProgressStatuses: PackagingSObjects.Package2VersionStatus[];
54
54
  export type PackageVersionCreateRequestError = {
@@ -155,6 +155,7 @@ export type PackageVersionListOptions = {
155
155
  verbose?: boolean;
156
156
  concise?: boolean;
157
157
  isReleased?: boolean;
158
+ showConversionsOnly?: boolean;
158
159
  };
159
160
  export type PackageVersionUpdateOptions = {
160
161
  InstallKey?: string;
@@ -266,10 +267,12 @@ export type PackageVersionCreateRequestQueryOptions = {
266
267
  createdlastdays?: number;
267
268
  status?: 'Queued' | 'InProgress' | 'Success' | 'Error';
268
269
  id?: string;
270
+ showConversionsOnly?: boolean;
269
271
  };
270
272
  export type ProfileApiOptions = {
271
273
  project: SfProject;
272
274
  includeUserLicenses: boolean;
275
+ generateProfileInformation: boolean;
273
276
  };
274
277
  export type PackageVersionReportResult = Partial<Omit<PackagingSObjects.Package2Version, 'AncestorId' | 'HasPassedCodeCoverageCheck' | 'HasMetadataRemoved'>> & {
275
278
  Package2: Partial<Omit<PackagingSObjects.Package2, 'IsOrgDependent'>> & {
@@ -370,4 +373,3 @@ export declare const Package1VersionEvents: {
370
373
  progress: string;
371
374
  };
372
375
  };
373
- export type PackageXml = Pick<Package, 'types' | 'version'>;
@@ -1,27 +1,63 @@
1
1
  import { SfProject } from '@salesforce/core';
2
2
  import { AsyncCreatable } from '@salesforce/kit';
3
- import { PackageXml, ProfileApiOptions } from '../interfaces';
3
+ import { ProfileApiOptions } from '../interfaces';
4
4
  export declare class PackageProfileApi extends AsyncCreatable<ProfileApiOptions> {
5
+ readonly profiles: ProfileInformation[];
6
+ nodeEntities: {
7
+ name: string[];
8
+ parentElement: string[];
9
+ childElement: string[];
10
+ };
11
+ otherProfileSettings: {
12
+ name: string[];
13
+ parentElement: string[];
14
+ childElement: string[];
15
+ };
5
16
  project: SfProject;
6
17
  includeUserLicenses: boolean;
18
+ generateProfileInformation: boolean;
7
19
  constructor(options: ProfileApiOptions);
8
20
  init(): Promise<void>;
9
21
  /**
10
22
  * For any profile present in the workspace, this function generates a subset of data that only contains references
11
23
  * to items in the manifest.
12
24
  *
13
- * return a list of profile file locations that need to be removed from the package because they are empty
14
- *
15
25
  * @param destPath location of new profiles
16
- * @param manifestTypes: array of objects { name: string, members: string[] } that represent package xml types
26
+ * @param manifest
17
27
  * @param excludedDirectories Directories to not include profiles from
18
28
  */
19
- generateProfiles(destPath: string, manifestTypes: PackageXml['types'], excludedDirectories?: string[]): string[];
29
+ generateProfiles(destPath: string, manifest: {
30
+ Package: Array<{
31
+ name: string[];
32
+ members: string[];
33
+ }>;
34
+ }, excludedDirectories?: string[]): string[];
20
35
  /**
21
36
  * Filter out all profiles in the manifest and if any profiles exists in the workspace, add them to the manifest.
22
37
  *
23
38
  * @param typesArr array of objects { name[], members[] } that represent package types JSON.
24
39
  * @param excludedDirectories Direcotires not to generate profiles for
25
40
  */
26
- filterAndGenerateProfilesForManifest(typesArr: PackageXml['types'], excludedDirectories?: string[]): PackageXml['types'];
41
+ filterAndGenerateProfilesForManifest(typesArr: Array<{
42
+ name: string[];
43
+ members: string[];
44
+ }>, excludedDirectories?: string[]): Array<{
45
+ name: string[];
46
+ members: string[];
47
+ }>;
48
+ getProfileInformation(): ProfileInformation[];
49
+ private copyNodes;
50
+ private findAllProfiles;
51
+ }
52
+ declare class ProfileInformation {
53
+ ProfileName: string;
54
+ ProfilePath: string;
55
+ IsPackaged: boolean;
56
+ settingsRemoved: string[];
57
+ constructor(ProfileName: string, ProfilePath: string, IsPackaged: boolean, settingsRemoved: string[]);
58
+ setIsPackaged(IsPackaged: boolean): void;
59
+ appendRemovedSetting(setting: string): void;
60
+ logDebug(): string;
61
+ logInfo(): string;
27
62
  }
63
+ export {};
@@ -1,20 +1,77 @@
1
1
  "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PackageProfileApi = void 0;
4
2
  /*
5
3
  * Copyright (c) 2022, salesforce.com, inc.
6
4
  * All rights reserved.
7
5
  * Licensed under the BSD 3-Clause license.
8
6
  * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
9
7
  */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.PackageProfileApi = void 0;
10
10
  const path = require("path");
11
11
  const fs = require("fs");
12
12
  const glob = require("glob");
13
+ const xmldom_1 = require("@xmldom/xmldom");
13
14
  const core_1 = require("@salesforce/core");
14
15
  const kit_1 = require("@salesforce/kit");
15
- const profileRewriter_1 = require("./profileRewriter");
16
16
  core_1.Messages.importMessagesDirectory(__dirname);
17
17
  const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging', 'profile_api');
18
+ // nodeEntities is used to determine which elements in the profile are relevant to the source being packaged.
19
+ // name refers to the entity type name in source that the element pertains to. As an example, a profile may
20
+ // have an entry like the example below, which should only be added to the packaged profile if the related
21
+ // CustomObject is in the source being packaged:
22
+ // <objectPermissions>
23
+ // <allowCreate>true</allowCreate>
24
+ // ...
25
+ // <object>MyCustomObject__c</object>
26
+ // ...
27
+ // </objectPermissions>
28
+ //
29
+ // For this example: nodeEntities.parentElement = objectPermissions and nodeEntities.childElement = object
30
+ const NODE_ENTITIES = {
31
+ name: [
32
+ 'CustomObject',
33
+ 'CustomField',
34
+ 'Layout',
35
+ 'CustomTab',
36
+ 'CustomApplication',
37
+ 'ApexClass',
38
+ 'CustomPermission',
39
+ 'ApexPage',
40
+ 'ExternalDataSource',
41
+ 'RecordType',
42
+ ],
43
+ parentElement: [
44
+ 'objectPermissions',
45
+ 'fieldPermissions',
46
+ 'layoutAssignments',
47
+ 'tabVisibilities',
48
+ 'applicationVisibilities',
49
+ 'classAccesses',
50
+ 'customPermissions',
51
+ 'pageAccesses',
52
+ 'externalDataSourceAccesses',
53
+ 'recordTypeVisibilities',
54
+ ],
55
+ childElement: [
56
+ 'object',
57
+ 'field',
58
+ 'layout',
59
+ 'tab',
60
+ 'application',
61
+ 'apexClass',
62
+ 'name',
63
+ 'apexPage',
64
+ 'externalDataSource',
65
+ 'recordType',
66
+ ],
67
+ };
68
+ // There are some profile settings that are allowed to be packaged that may not necessarily map to a specific metadata
69
+ // object. We should still handle these accordingly, but a little differently than the above mentioned types.
70
+ const OTHER_PROFILE_SETTINGS = {
71
+ name: ['CustomSettings', 'CustomMetadataTypeAccess'],
72
+ parentElement: ['customSettingAccesses', 'customMetadataTypeAccesses'],
73
+ childElement: ['name', 'name'],
74
+ };
18
75
  /*
19
76
  * This class provides functions used to re-write .profiles in the workspace when creating a package2 version.
20
77
  * All profiles found in the workspaces are extracted out and then re-written to only include metadata in the profile
@@ -23,9 +80,14 @@ const profileApiMessages = core_1.Messages.loadMessages('@salesforce/packaging',
23
80
  class PackageProfileApi extends kit_1.AsyncCreatable {
24
81
  constructor(options) {
25
82
  super(options);
83
+ this.profiles = [];
84
+ this.nodeEntities = NODE_ENTITIES;
85
+ this.otherProfileSettings = OTHER_PROFILE_SETTINGS;
26
86
  this.includeUserLicenses = false;
87
+ this.generateProfileInformation = false;
27
88
  this.project = options.project;
28
89
  this.includeUserLicenses = options.includeUserLicenses ?? false;
90
+ this.generateProfileInformation = options.generateProfileInformation ?? false;
29
91
  }
30
92
  // eslint-disable-next-line class-methods-use-this,@typescript-eslint/no-empty-function
31
93
  async init() { }
@@ -33,45 +95,91 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
33
95
  * For any profile present in the workspace, this function generates a subset of data that only contains references
34
96
  * to items in the manifest.
35
97
  *
36
- * return a list of profile file locations that need to be removed from the package because they are empty
37
- *
38
98
  * @param destPath location of new profiles
39
- * @param manifestTypes: array of objects { name: string, members: string[] } that represent package xml types
99
+ * @param manifest
40
100
  * @param excludedDirectories Directories to not include profiles from
41
101
  */
42
- generateProfiles(destPath, manifestTypes, excludedDirectories = []) {
43
- const logger = core_1.Logger.childFromRoot('PackageProfileApi');
44
- return (getProfilesWithNamesAndPaths({
45
- projectPath: this.project.getPath(),
46
- excludedDirectories,
47
- })
48
- .map(({ profilePath, name: profileName }) => {
49
- const originalProfile = (0, profileRewriter_1.profileStringToProfile)(fs.readFileSync(profilePath, 'utf-8'));
50
- const adjustedProfile = (0, profileRewriter_1.profileRewriter)(originalProfile, (0, profileRewriter_1.manifestTypesToMap)(manifestTypes), this.includeUserLicenses);
51
- return {
52
- profileName,
53
- profilePath,
54
- hasContent: Object.keys(adjustedProfile).length,
55
- adjustedProfile,
56
- removedSettings: getRemovedSettings(originalProfile, adjustedProfile),
57
- xmlFileLocation: getXmlFileLocation(destPath, profilePath),
58
- };
59
- })
60
- // side effect: modify profiles in place
61
- .filter(({ hasContent, profileName, removedSettings, profilePath, xmlFileLocation, adjustedProfile }) => {
62
- if (!hasContent) {
63
- logger.warn(`Profile ${profileName} has no content after filtering. It will still be part of the package but you can remove if it it's not needed.`);
64
- return true;
65
- }
66
- else {
67
- logger.info(profileApiMessages.getMessage('addProfileToPackage', [profileName, profilePath]));
68
- removedSettings.forEach((setting) => {
69
- logger.info(profileApiMessages.getMessage('removeProfileSetting', [setting, profileName]));
102
+ generateProfiles(destPath, manifest, excludedDirectories = []) {
103
+ const excludedProfiles = [];
104
+ const profilePaths = this.findAllProfiles(excludedDirectories);
105
+ if (!profilePaths) {
106
+ return excludedProfiles;
107
+ }
108
+ profilePaths.forEach((profilePath) => {
109
+ // profile metadata can present in any directory in the package structure
110
+ const profileNameMatch = profilePath.match(/([^/]+)\.profile-meta.xml/);
111
+ const profileName = profileNameMatch ? profileNameMatch[1] : null;
112
+ if (profileName) {
113
+ const profileDom = new xmldom_1.DOMParser().parseFromString(fs.readFileSync(profilePath, 'utf-8'));
114
+ const newDom = new xmldom_1.DOMParser().parseFromString('<?xml version="1.0" encoding="UTF-8"?><Profile xmlns="http://soap.sforce.com/2006/04/metadata"></Profile>');
115
+ const profileNode = newDom.getElementsByTagName('Profile')[0];
116
+ let hasNodes = false;
117
+ // We need to keep track of all the members for when we package up the "OtherProfileSettings"
118
+ let allMembers = [];
119
+ manifest.Package.forEach((element) => {
120
+ const name = element.name;
121
+ const members = element.members;
122
+ allMembers = allMembers.concat(members);
123
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
124
+ const idx = this.nodeEntities.name.indexOf(name[0]);
125
+ if (idx > -1) {
126
+ hasNodes =
127
+ this.copyNodes(profileDom, this.nodeEntities.parentElement[idx], this.nodeEntities.childElement[idx], members, profileNode, profileName) || hasNodes;
128
+ }
129
+ });
130
+ // Go through each of the other profile settings we might want to include. We pass in "all" the members since these
131
+ // will reference anything that could be packaged. The "copyNodes" function will only include the type if it
132
+ // exists in the profile itself
133
+ this.otherProfileSettings.name.forEach((element) => {
134
+ const idx = this.otherProfileSettings.name.indexOf(element);
135
+ if (idx > -1) {
136
+ hasNodes =
137
+ this.copyNodes(profileDom, this.otherProfileSettings.parentElement[idx], this.otherProfileSettings.childElement[idx], allMembers, profileNode, profileName) || hasNodes;
138
+ }
70
139
  });
71
- fs.writeFileSync(xmlFileLocation, (0, profileRewriter_1.profileObjectToString)(adjustedProfile), 'utf-8');
140
+ // add userLicenses to the profile
141
+ if (this.includeUserLicenses) {
142
+ const userLicenses = profileDom.getElementsByTagName('userLicense');
143
+ if (userLicenses) {
144
+ hasNodes = true;
145
+ for (const userLicense of Array.from(userLicenses)) {
146
+ profileNode.appendChild(userLicense.cloneNode(true));
147
+ }
148
+ }
149
+ }
150
+ const xmlSrcFile = path.basename(profilePath);
151
+ const xmlFile = xmlSrcFile.replace(/(.*)(-meta.xml)/, '$1');
152
+ const destFilePath = path.join(destPath, xmlFile);
153
+ if (hasNodes) {
154
+ const serializer = new xmldom_1.XMLSerializer();
155
+ serializer.serializeToString(newDom);
156
+ fs.writeFileSync(destFilePath, serializer.serializeToString(newDom), 'utf-8');
157
+ }
158
+ else {
159
+ // remove from manifest
160
+ // eslint-disable-next-line @typescript-eslint/no-shadow
161
+ const profileName = xmlFile.replace(/(.*)(\.profile)/, '$1');
162
+ excludedProfiles.push(profileName);
163
+ if (this.generateProfileInformation) {
164
+ const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
165
+ if (profile) {
166
+ profile.setIsPackaged(false);
167
+ }
168
+ }
169
+ try {
170
+ fs.unlinkSync(destFilePath);
171
+ }
172
+ catch (err) {
173
+ // It is normal for the file to not exist if the profile is in the workspace but not in the directory being packaged.
174
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
175
+ if (err instanceof Error && 'code' in err && err.code !== 'ENOENT') {
176
+ throw err;
177
+ }
178
+ }
179
+ }
72
180
  }
73
- })
74
- .map(({ xmlFileLocation }) => xmlFileLocation.replace(/(.*)(\.profile)/, '$1')));
181
+ });
182
+ return excludedProfiles;
75
183
  }
76
184
  /**
77
185
  * Filter out all profiles in the manifest and if any profiles exists in the workspace, add them to the manifest.
@@ -80,29 +188,97 @@ class PackageProfileApi extends kit_1.AsyncCreatable {
80
188
  * @param excludedDirectories Direcotires not to generate profiles for
81
189
  */
82
190
  filterAndGenerateProfilesForManifest(typesArr, excludedDirectories = []) {
83
- const profilePathsWithNames = getProfilesWithNamesAndPaths({
84
- projectPath: this.project.getPath(),
85
- excludedDirectories,
191
+ const profilePaths = this.findAllProfiles(excludedDirectories);
192
+ // Filter all profiles
193
+ const filteredTypesArr = (typesArr ?? []).filter((kvp) => kvp.name[0] !== 'Profile');
194
+ if (profilePaths) {
195
+ const members = [];
196
+ profilePaths.forEach((profilePath) => {
197
+ // profile metadata can present in any directory in the package structure
198
+ const profileNameMatch = profilePath.match(/([^/]+)\.profile-meta.xml/);
199
+ const profileName = profileNameMatch ? profileNameMatch[1] : null;
200
+ if (profileName) {
201
+ members.push(profileName);
202
+ if (this.generateProfileInformation) {
203
+ this.profiles.push(new ProfileInformation(profileName, profilePath, true, []));
204
+ }
205
+ }
206
+ });
207
+ if (members.length > 0) {
208
+ filteredTypesArr.push({ name: ['Profile'], members });
209
+ }
210
+ }
211
+ return filteredTypesArr;
212
+ }
213
+ getProfileInformation() {
214
+ return this.profiles;
215
+ }
216
+ copyNodes(originalDom, parentElement, childElement, members, appendToNode, profileName) {
217
+ let nodesAdded = false;
218
+ const nodes = originalDom.getElementsByTagName(parentElement);
219
+ if (!nodes) {
220
+ return nodesAdded;
221
+ }
222
+ // eslint-disable-next-line @typescript-eslint/prefer-for-of
223
+ for (let i = 0; i < nodes.length; i++) {
224
+ const name = nodes[i].getElementsByTagName(childElement)[0].childNodes[0].nodeValue;
225
+ if (name) {
226
+ if (members.includes(name)) {
227
+ // appendChild will take the passed in node (newNode) and find the parent if it exists and then remove
228
+ // the newNode from the parent. This causes issues with the way this is copying the nodes, so pass in a clone instead.
229
+ const currentNode = nodes[i].cloneNode(true);
230
+ appendToNode.appendChild(currentNode);
231
+ nodesAdded = true;
232
+ }
233
+ else if (this.generateProfileInformation) {
234
+ // Tell the user which profile setting has been removed from the package
235
+ const profile = this.profiles.find(({ ProfileName }) => ProfileName === profileName);
236
+ if (profile) {
237
+ profile.appendRemovedSetting(name);
238
+ }
239
+ }
240
+ }
241
+ }
242
+ return nodesAdded;
243
+ }
244
+ findAllProfiles(excludedDirectories = []) {
245
+ return glob.sync(path.join(this.project.getPath(), '**', '*.profile-meta.xml'), {
246
+ ignore: excludedDirectories.map((dir) => `**/${dir}/**`),
86
247
  });
87
- // Filter all profiles, and add back the ones we found names for
88
- return typesArr
89
- .filter((kvp) => kvp.name !== 'Profile')
90
- .concat([{ name: 'Profile', members: profilePathsWithNames.map((i) => i.name) }]);
91
248
  }
92
249
  }
93
250
  exports.PackageProfileApi = PackageProfileApi;
94
- const findAllProfiles = ({ projectPath, excludedDirectories = [], }) => glob.sync(path.join(projectPath, '**', '*.profile-meta.xml'), {
95
- ignore: excludedDirectories.map((dir) => `**/${dir}/**`),
96
- });
97
- const isProfilePathWithName = (profilePathWithName) => typeof profilePathWithName.name === 'string';
98
- const profilePathToName = (profilePath) => profilePath.match(/([^/]+)\.profile-meta.xml/)?.[1];
99
- const getProfilesWithNamesAndPaths = ({ projectPath, excludedDirectories, }) => findAllProfiles({ projectPath, excludedDirectories })
100
- .map((profilePath) => ({ profilePath, name: profilePathToName(profilePath) }))
101
- .filter(isProfilePathWithName);
102
- const getXmlFileLocation = (destPath, profilePath) => path.join(destPath, path.basename(profilePath).replace(/(.*)(-meta.xml)/, '$1'));
103
- const getRemovedSettings = (originalProfile, adjustedProfile) => {
104
- const originalProfileSettings = Object.keys(originalProfile);
105
- const adjustedProfileSettings = new Set(Object.keys(adjustedProfile));
106
- return originalProfileSettings.filter((setting) => !adjustedProfileSettings.has(setting));
107
- };
251
+ class ProfileInformation {
252
+ constructor(ProfileName, ProfilePath, IsPackaged, settingsRemoved) {
253
+ this.ProfileName = ProfileName;
254
+ this.ProfilePath = ProfilePath;
255
+ this.IsPackaged = IsPackaged;
256
+ this.settingsRemoved = settingsRemoved;
257
+ }
258
+ setIsPackaged(IsPackaged) {
259
+ this.IsPackaged = IsPackaged;
260
+ }
261
+ appendRemovedSetting(setting) {
262
+ this.settingsRemoved.push(setting);
263
+ }
264
+ logDebug() {
265
+ let info = profileApiMessages.getMessage('addProfileToPackage', [this.ProfileName, this.ProfilePath]);
266
+ this.settingsRemoved.forEach((setting) => {
267
+ info += '\n\t' + profileApiMessages.getMessage('removeProfileSetting', [setting, this.ProfileName]);
268
+ });
269
+ if (!this.IsPackaged) {
270
+ info += '\n\t' + profileApiMessages.getMessage('removeProfile', [this.ProfileName]);
271
+ }
272
+ info += '\n';
273
+ return info;
274
+ }
275
+ logInfo() {
276
+ if (this.IsPackaged) {
277
+ return profileApiMessages.getMessage('addProfileToPackage', [this.ProfileName, this.ProfilePath]);
278
+ }
279
+ else {
280
+ return profileApiMessages.getMessage('profileNotIncluded', [this.ProfileName]);
281
+ }
282
+ }
283
+ }
108
284
  //# sourceMappingURL=packageProfileApi.js.map
@@ -1,5 +1,5 @@
1
1
  import { ConvertResult } from '@salesforce/source-deploy-retrieve';
2
- import { MDFolderForArtifactOptions, PackageVersionCreateOptions, PackageVersionCreateRequestResult, PackageXml } from '../interfaces';
2
+ import { MDFolderForArtifactOptions, PackageVersionCreateOptions, PackageVersionCreateRequestResult } from '../interfaces';
3
3
  export declare class PackageVersionCreate {
4
4
  private options;
5
5
  private apiVersionFromPackageXml;
@@ -66,8 +66,3 @@ export declare class MetadataResolver {
66
66
  */
67
67
  private convertMetadata;
68
68
  }
69
- export declare const packageXmlStringToPackageXmlJson: (rawXml: string) => PackageXml;
70
- /**
71
- * Converts PackageXmlJson to a string representing the Xml
72
- * */
73
- export declare const packageXmlJsonToXmlString: (packageXmlJson: PackageXml) => string;
@@ -6,15 +6,15 @@
6
6
  * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
7
7
  */
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.packageXmlJsonToXmlString = exports.packageXmlStringToPackageXmlJson = exports.MetadataResolver = exports.PackageVersionCreate = void 0;
9
+ exports.MetadataResolver = exports.PackageVersionCreate = void 0;
10
10
  const path = require("path");
11
11
  const os = require("os");
12
12
  const fs = require("fs");
13
13
  const core_1 = require("@salesforce/core");
14
14
  const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
15
15
  const scratchOrgSettingsGenerator_1 = require("@salesforce/core/lib/org/scratchOrgSettingsGenerator");
16
+ const xml2js = require("xml2js");
16
17
  const kit_1 = require("@salesforce/kit");
17
- const fast_xml_parser_1 = require("fast-xml-parser");
18
18
  const pkgUtils = require("../utils/packageUtils");
19
19
  const packageUtils_1 = require("../utils/packageUtils");
20
20
  const interfaces_1 = require("../interfaces");
@@ -240,6 +240,8 @@ class PackageVersionCreate {
240
240
  sourceDir: sourceBaseDir,
241
241
  sourceApiVersion: this.project?.getSfProjectJson()?.get('sourceApiVersion') ?? undefined,
242
242
  };
243
+ // Stores any additional client side info that might be needed later on in the process
244
+ const clientSideInfo = new Map();
243
245
  await fs.promises.mkdir(packageVersBlobDirectory, { recursive: true });
244
246
  const settingsGenerator = new scratchOrgSettingsGenerator_1.default({ asDirectory: true });
245
247
  let packageDescriptorJson = (0, kit_1.cloneJson)(this.packageObject);
@@ -303,19 +305,7 @@ class PackageVersionCreate {
303
305
  }
304
306
  packageDescriptorJson.ancestorId = ancestorId;
305
307
  await fs.promises.writeFile(path.join(packageVersBlobDirectory, DESCRIPTOR_FILE), JSON.stringify(packageDescriptorJson), 'utf-8');
306
- await this.cleanGeneratedPackage({
307
- packageVersMetadataFolder,
308
- packageVersProfileFolder,
309
- unpackagedMetadataFolder,
310
- seedMetadataFolder,
311
- metadataZipFile,
312
- settingsZipFile,
313
- packageVersBlobDirectory,
314
- packageVersBlobZipFile,
315
- unpackagedMetadataZipFile,
316
- seedMetadataZipFile,
317
- settingsGenerator,
318
- });
308
+ await this.cleanGeneratedPackage(packageVersMetadataFolder, packageVersProfileFolder, unpackagedMetadataFolder, seedMetadataFolder, metadataZipFile, settingsZipFile, packageVersBlobDirectory, packageVersBlobZipFile, unpackagedMetadataZipFile, seedMetadataZipFile, clientSideInfo, settingsGenerator);
319
309
  return this.createRequestObject(preserveFiles, packageVersTmpRoot, packageVersBlobZipFile);
320
310
  }
321
311
  verifyHasSource(componentSet) {
@@ -323,22 +313,22 @@ class PackageVersionCreate {
323
313
  throw messages.createError('noSourceInRootDirectory', [this.packageObject.path ?? '<unknown>']);
324
314
  }
325
315
  }
326
- async cleanGeneratedPackage({ packageVersMetadataFolder, packageVersProfileFolder, unpackagedMetadataFolder, seedMetadataFolder, metadataZipFile, settingsZipFile, packageVersBlobDirectory, packageVersBlobZipFile, unpackagedMetadataZipFile, seedMetadataZipFile, settingsGenerator, }) {
316
+ async cleanGeneratedPackage(packageVersMetadataFolder, packageVersProfileFolder, unpackagedMetadataFolder, seedMetadataFolder, metadataZipFile, settingsZipFile, packageVersBlobDirectory, packageVersBlobZipFile, unpackagedMetadataZipFile, seedMetadataZipFile, clientSideInfo, settingsGenerator) {
327
317
  // As part of the source convert process, the package.xml has been written into the tmp metadata directory.
328
318
  // The package.xml may need to be manipulated due to processing profiles in the workspace or additional
329
319
  // metadata exclusions. If necessary, read the existing package.xml and then re-write it.
330
320
  const currentPackageXml = await fs.promises.readFile(path.join(packageVersMetadataFolder, 'package.xml'), 'utf8');
331
321
  // convert to json
332
- const packageXmlAsJson = (0, exports.packageXmlStringToPackageXmlJson)(currentPackageXml);
333
- if (!packageXmlAsJson) {
322
+ const packageJson = (await xml2js.parseStringPromise(currentPackageXml));
323
+ if (!packageJson?.Package) {
334
324
  throw messages.createError('packageXmlDoesNotContainPackage');
335
325
  }
336
- if (!packageXmlAsJson?.types) {
326
+ if (!packageJson?.Package.types) {
337
327
  throw messages.createError('packageXmlDoesNotContainPackageTypes');
338
328
  }
339
329
  fs.mkdirSync(packageVersMetadataFolder, { recursive: true });
340
330
  fs.mkdirSync(packageVersProfileFolder, { recursive: true });
341
- this.apiVersionFromPackageXml = packageXmlAsJson.version;
331
+ this.apiVersionFromPackageXml = packageJson.Package.version;
342
332
  const sourceApiVersion = this.project?.getSfProjectJson()?.get('sourceApiVersion');
343
333
  const hasSeedMetadata = await this.metadataResolver.resolveMetadata(this.packageObject.seedMetadata?.path, seedMetadataFolder, 'seedMDDirectoryDoesNotExist', sourceApiVersion);
344
334
  let hasUnpackagedMetadata = false;
@@ -351,16 +341,32 @@ class PackageVersionCreate {
351
341
  .getPackageDirectories()
352
342
  .map((packageDir) => packageDir.unpackagedMetadata?.path)
353
343
  .filter((packageDirPath) => packageDirPath);
354
- const typesArr = this.options?.profileApi?.filterAndGenerateProfilesForManifest(packageXmlAsJson.types, profileExcludeDirs) ??
355
- packageXmlAsJson.types;
344
+ const typesArr = this.options?.profileApi?.filterAndGenerateProfilesForManifest(packageJson.Package.types, profileExcludeDirs) ??
345
+ packageJson.Package.types;
356
346
  // Next generate profiles and retrieve any profiles that were excluded because they had no matching nodes.
357
- const excludedProfiles = this.options?.profileApi?.generateProfiles(packageVersProfileFolder, typesArr, profileExcludeDirs);
358
- packageXmlAsJson.types = typesArr.map((type) => {
359
- if (type.name !== 'Profile')
360
- return type;
361
- return { ...type, members: type.members.filter((m) => !excludedProfiles?.includes(m)) };
347
+ const excludedProfiles = this.options?.profileApi?.generateProfiles(packageVersProfileFolder, {
348
+ Package: typesArr,
349
+ }, profileExcludeDirs);
350
+ if (excludedProfiles?.length) {
351
+ const profileIdx = typesArr.findIndex((e) => e.name[0] === 'Profile');
352
+ typesArr[profileIdx].members = typesArr[profileIdx].members.filter((e) => !excludedProfiles.includes(e));
353
+ }
354
+ packageJson.Package.types = typesArr;
355
+ // Re-write the package.xml in case profiles have been added or removed
356
+ const xmlBuilder = new xml2js.Builder({
357
+ xmldec: { version: '1.0', encoding: 'UTF-8' },
358
+ });
359
+ const xml = xmlBuilder.buildObject(packageJson);
360
+ // Log information about the profiles being packaged up
361
+ const profiles = this.options?.profileApi?.getProfileInformation() ?? [];
362
+ profiles.forEach((profile) => {
363
+ if (this.logger.shouldLog(core_1.LoggerLevel.DEBUG)) {
364
+ this.logger.debug(profile.logDebug());
365
+ }
366
+ else if (this.logger.shouldLog(core_1.LoggerLevel.INFO)) {
367
+ this.logger.info(profile.logInfo());
368
+ }
362
369
  });
363
- const xml = (0, exports.packageXmlJsonToXmlString)(packageXmlAsJson);
364
370
  await fs.promises.writeFile(path.join(packageVersMetadataFolder, 'package.xml'), xml, 'utf-8');
365
371
  // Zip the packageVersMetadataFolder folder and put the zip in {packageVersBlobDirectory}/package.zip
366
372
  await (0, packageUtils_1.zipDir)(packageVersMetadataFolder, metadataZipFile);
@@ -506,9 +512,11 @@ class PackageVersionCreate {
506
512
  return this.pkg.getType();
507
513
  }
508
514
  async resolveUserLicenses(includeUserLicenses) {
515
+ const shouldGenerateProfileInformation = this.logger.shouldLog(core_1.LoggerLevel.INFO) || this.logger.shouldLog(core_1.LoggerLevel.DEBUG);
509
516
  return packageProfileApi_1.PackageProfileApi.create({
510
517
  project: this.project,
511
518
  includeUserLicenses,
519
+ generateProfileInformation: shouldGenerateProfileInformation,
512
520
  });
513
521
  }
514
522
  async validateOptionsForPackageType() {
@@ -847,39 +855,4 @@ class MetadataResolver {
847
855
  }
848
856
  }
849
857
  exports.MetadataResolver = MetadataResolver;
850
- const packageXmlStringToPackageXmlJson = (rawXml) => {
851
- const parser = new fast_xml_parser_1.XMLParser({
852
- ignoreAttributes: true,
853
- parseTagValue: false,
854
- parseAttributeValue: false,
855
- cdataPropName: '__cdata',
856
- ignoreDeclaration: true,
857
- numberParseOptions: { leadingZeros: false, hex: false },
858
- // make sure types and members is always an array
859
- isArray: (name) => ['types', 'members'].includes(name),
860
- });
861
- return parser.parse(rawXml).Package;
862
- };
863
- exports.packageXmlStringToPackageXmlJson = packageXmlStringToPackageXmlJson;
864
- /**
865
- * Converts PackageXmlJson to a string representing the Xml
866
- * */
867
- const packageXmlJsonToXmlString = (packageXmlJson) => {
868
- const builder = new fast_xml_parser_1.XMLBuilder({
869
- format: true,
870
- indentBy: ' ',
871
- ignoreAttributes: false,
872
- cdataPropName: '__cdata',
873
- processEntities: false,
874
- attributeNamePrefix: '@@@',
875
- });
876
- return String(builder.build({
877
- '?xml': {
878
- '@@@version': '1.0',
879
- '@@@encoding': 'UTF-8',
880
- },
881
- Package: { ...packageXmlJson, '@@@xmlns': 'http://soap.sforce.com/2006/04/metadata' },
882
- }));
883
- };
884
- exports.packageXmlJsonToXmlString = packageXmlJsonToXmlString;
885
858
  //# sourceMappingURL=packageVersionCreate.js.map
@@ -14,7 +14,7 @@ core_1.Messages.importMessagesDirectory(__dirname);
14
14
  const messages = core_1.Messages.loadMessages('@salesforce/packaging', 'package_version_create');
15
15
  const STATUS_ERROR = 'Error';
16
16
  const QUERY = 'SELECT Id, Status, Package2Id, Package2VersionId, Package2Version.SubscriberPackageVersionId, Tag, Branch, ' +
17
- 'CreatedDate, Package2Version.HasMetadataRemoved, CreatedById ' +
17
+ 'CreatedDate, Package2Version.HasMetadataRemoved, CreatedById, IsConversionRequest, Package2Version.ConvertedFromVersionId ' +
18
18
  'FROM Package2VersionCreateRequest ' +
19
19
  '%s' + // WHERE, if applicable
20
20
  'ORDER BY CreatedDate desc';
@@ -58,8 +58,23 @@ async function query(query, connection) {
58
58
  CreatedDate: formatDate(new Date(record.CreatedDate)),
59
59
  HasMetadataRemoved: record.Package2Version != null ? record.Package2Version.HasMetadataRemoved : null,
60
60
  CreatedBy: record.CreatedById,
61
+ ConvertedFromVersionId: convertedFromVersionMessage(record.Status, record.Package2Version?.ConvertedFromVersionId),
61
62
  }));
62
63
  }
64
+ function convertedFromVersionMessage(status, convertedFromVersionId) {
65
+ switch (status) {
66
+ case 'Success':
67
+ return convertedFromVersionId;
68
+ case 'Queued':
69
+ return messages.getMessage('IdUnavailableWhenQueued');
70
+ case 'InProgress':
71
+ return messages.getMessage('IdUnavailableWhenInProgress');
72
+ case 'Error':
73
+ return messages.getMessage('IdUnavailableWhenError');
74
+ default:
75
+ return messages.getMessage('IdUnavailableWhenInProgress');
76
+ }
77
+ }
63
78
  async function queryForErrors(packageVersionCreateRequestId, connection) {
64
79
  const queryResult = await connection.tooling.query(`SELECT Message FROM Package2VersionCreateRequestError WHERE ParentRequest.Id = '${packageVersionCreateRequestId}'`);
65
80
  return queryResult.records ? queryResult.records.map((record) => record.Message) : [];
@@ -80,6 +95,10 @@ function constructWhere(options) {
80
95
  if (options?.status) {
81
96
  where.push(`Status = '${options.status.toLowerCase()}'`);
82
97
  }
98
+ // show only conversions
99
+ if (options?.showConversionsOnly) {
100
+ where.push('IsConversionRequest = true ');
101
+ }
83
102
  return where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
84
103
  }
85
104
  //# sourceMappingURL=packageVersionCreateRequest.js.map
@@ -34,11 +34,11 @@ const defaultFields = [
34
34
  'AncestorId',
35
35
  'ValidationSkipped',
36
36
  'CreatedById',
37
+ 'ConvertedFromVersionId',
37
38
  ];
38
39
  const verboseFields = [
39
40
  'CodeCoverage',
40
41
  'HasPassedCodeCoverageCheck',
41
- 'ConvertedFromVersionId',
42
42
  'ReleaseVersion',
43
43
  'BuildDurationInSeconds',
44
44
  'HasMetadataRemoved',
@@ -107,6 +107,9 @@ function constructWhere(options) {
107
107
  if (options?.isReleased) {
108
108
  where.push('IsReleased = true');
109
109
  }
110
+ if (options?.showConversionsOnly) {
111
+ where.push('ConvertedFromVersionId != null');
112
+ }
110
113
  // exclude deleted
111
114
  where.push('IsDeprecated = false');
112
115
  return where;
@@ -14,7 +14,7 @@ const core_1 = require("@salesforce/core");
14
14
  const pkgUtils = require("../utils/packageUtils");
15
15
  const QUERY = 'SELECT Package2Id, SubscriberPackageVersionId, Name, Description, Tag, Branch, AncestorId, ValidationSkipped, ' +
16
16
  'MajorVersion, MinorVersion, PatchVersion, BuildNumber, IsReleased, CodeCoverage, HasPassedCodeCoverageCheck, ' +
17
- 'Package2.IsOrgDependent, ReleaseVersion, BuildDurationInSeconds, HasMetadataRemoved, CreatedById ' +
17
+ 'Package2.IsOrgDependent, ReleaseVersion, BuildDurationInSeconds, HasMetadataRemoved, CreatedById, ConvertedFromVersionId ' +
18
18
  'FROM Package2Version ' +
19
19
  "WHERE Id = '%s' AND IsDeprecated != true " +
20
20
  'ORDER BY Package2Id, Branch, MajorVersion, MinorVersion, PatchVersion, BuildNumber';
@@ -194,3 +194,15 @@ The version number is required and was not found in the options or in package js
194
194
  # missingConnection
195
195
 
196
196
  A connection is required.
197
+
198
+ # IdUnavailableWhenQueued
199
+
200
+ Request is queued. ID unavailable.
201
+
202
+ # IdUnavailableWhenInProgress
203
+
204
+ Request is in progress. ID unavailable.
205
+
206
+ # IdUnavailableWhenError
207
+
208
+ ID Unavailable
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/packaging",
3
- "version": "2.2.9-profiles.1",
3
+ "version": "2.3.0",
4
4
  "description": "Packaging library for the Salesforce packaging platform",
5
5
  "main": "lib/exported",
6
6
  "types": "lib/exported.d.ts",
@@ -42,37 +42,44 @@
42
42
  ],
43
43
  "dependencies": {
44
44
  "@oclif/core": "^2.9.4",
45
- "@salesforce/core": "^4.3.10",
46
- "@salesforce/kit": "^3.0.5",
45
+ "@salesforce/core": "^4.3.2",
46
+ "@salesforce/kit": "^3.0.3",
47
47
  "@salesforce/schemas": "^1.6.0",
48
- "@salesforce/source-deploy-retrieve": "^9.3.4",
48
+ "@salesforce/source-deploy-retrieve": "^8.6.0",
49
49
  "@salesforce/ts-types": "^2.0.3",
50
- "fast-xml-parser": "^4.2.5",
50
+ "@xmldom/xmldom": "^0.8.9",
51
+ "debug": "^4.3.4",
51
52
  "globby": "^11",
52
53
  "graphology": "^0.25.1",
53
54
  "graphology-traversal": "^0.3.1",
54
55
  "graphology-types": "^0.24.7",
55
- "jsforce": "^2.0.0-beta.27",
56
- "jszip": "^3.10.1"
56
+ "js2xmlparser": "^4.0.2",
57
+ "jsforce": "^2.0.0-beta.23",
58
+ "jszip": "^3.10.1",
59
+ "ts-retry-promise": "^0.7.0",
60
+ "xml2js": "^0.6.0"
57
61
  },
58
62
  "devDependencies": {
59
63
  "@salesforce/cli-plugins-testkit": "^4.1.1",
60
64
  "@salesforce/dev-config": "^4.0.1",
61
65
  "@salesforce/dev-scripts": "^5.4.3",
62
66
  "@salesforce/prettier-config": "^0.0.3",
63
- "@salesforce/ts-sinon": "^1.4.11",
67
+ "@salesforce/ts-sinon": "^1.4.12",
68
+ "@types/debug": "4.1.8",
64
69
  "@types/globby": "^9.1.0",
65
70
  "@types/jszip": "^3.4.1",
71
+ "@types/xml2js": "^0.4.11",
66
72
  "@typescript-eslint/eslint-plugin": "^5.60.0",
67
73
  "@typescript-eslint/parser": "^5.61.0",
68
74
  "chai": "^4.3.7",
75
+ "commitizen": "^4.2.6",
69
76
  "eslint": "^8.44.0",
70
77
  "eslint-config-prettier": "^8.8.0",
71
78
  "eslint-config-salesforce": "^2.0.1",
72
79
  "eslint-config-salesforce-license": "^0.2.0",
73
80
  "eslint-config-salesforce-typescript": "^1.1.1",
74
81
  "eslint-plugin-header": "3.1.1",
75
- "eslint-plugin-import": "^2.27.5",
82
+ "eslint-plugin-import": "^2.28.0",
76
83
  "eslint-plugin-jsdoc": "^44.2.7",
77
84
  "eslint-plugin-sf-plugin": "^1.15.9",
78
85
  "husky": "^8.0.3",
@@ -1,37 +0,0 @@
1
- import { Profile } from 'jsforce/api/metadata';
2
- import { PackageXml } from '../interfaces';
3
- type ProfileCustomSettingAccess = {
4
- name: string;
5
- enabled: boolean;
6
- };
7
- export type CorrectedProfile = Profile & {
8
- customSettingAccesses: ProfileCustomSettingAccess[];
9
- };
10
- /**
11
- *
12
- * Takes a Profile that's been converted from package.xml to json.
13
- * Filters out all Profile props that are not
14
- * 1. used by packaging (ex: ipRanges)
15
- * 2. present in the package.xml (ex: ClassAccesses for a class not in the package)
16
- * 3. optionally retains the UserLicense prop only if the param is true
17
- *
18
- * @param profileJson json representation of a profile
19
- * @param packageXml package.xml as json
20
- * @param retainUserLicense boolean will preserve userLicense if true
21
- * @returns Profile
22
- */
23
- export declare const profileRewriter: (profileJson: CorrectedProfile, packageXml: PackageMap, retainUserLicense?: boolean) => CorrectedProfile;
24
- export declare const fieldCorrections: (fieldName: string) => string;
25
- /**
26
- * @param profileString: raw xml read from the file
27
- * @returns CorrectedProfile (json representation of the profile)
28
- */
29
- export declare const profileStringToProfile: (profileString: string) => CorrectedProfile;
30
- /** pass in an object that has the Profile props at the top level.
31
- * This function will add the outer wrapper `Profile` and convert the result to xml
32
- * */
33
- export declare const profileObjectToString: (profileObject: Partial<CorrectedProfile>) => string;
34
- /** it's easier to do lookups by Metadata Type on a Map */
35
- export declare const manifestTypesToMap: (original: PackageXml['types']) => PackageMap;
36
- type PackageMap = Map<string, string[]>;
37
- export {};
@@ -1,108 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.manifestTypesToMap = exports.profileObjectToString = exports.profileStringToProfile = exports.fieldCorrections = exports.profileRewriter = void 0;
4
- const fast_xml_parser_1 = require("fast-xml-parser");
5
- /**
6
- *
7
- * Takes a Profile that's been converted from package.xml to json.
8
- * Filters out all Profile props that are not
9
- * 1. used by packaging (ex: ipRanges)
10
- * 2. present in the package.xml (ex: ClassAccesses for a class not in the package)
11
- * 3. optionally retains the UserLicense prop only if the param is true
12
- *
13
- * @param profileJson json representation of a profile
14
- * @param packageXml package.xml as json
15
- * @param retainUserLicense boolean will preserve userLicense if true
16
- * @returns Profile
17
- */
18
- const profileRewriter = (profileJson, packageXml, retainUserLicense = false) => ({
19
- ...Object.fromEntries(Object.entries(profileJson)
20
- // remove settings that are not used for packaging
21
- .filter(isRewriteProp)
22
- // @ts-expect-error the previous filter restricts us to only things that appear in filterFunctions
23
- .map(([key, value]) => [key, filterFunctions[key]?.(value, packageXml)] ?? [])
24
- // some profileSettings might now be empty Arrays if the package.xml didn't have those types, so remove the entire property
25
- .filter(([, value]) => (Array.isArray(value) ? value.length : true))),
26
- // this one prop is controlled by a param. Put it back the way it was if the param is true
27
- ...(retainUserLicense && profileJson.userLicense ? { userLicense: profileJson.userLicense } : {}),
28
- });
29
- exports.profileRewriter = profileRewriter;
30
- // it's both a filter and a typeguard to make sure props are represented in filterFunctions
31
- const isRewriteProp = (prop) => rewriteProps.includes(prop[0]);
32
- const rewriteProps = [
33
- 'objectPermissions',
34
- 'fieldPermissions',
35
- 'layoutAssignments',
36
- 'applicationVisibilities',
37
- 'classAccesses',
38
- 'externalDataSourceAccesses',
39
- 'tabVisibilities',
40
- 'pageAccesses',
41
- 'customPermissions',
42
- 'customMetadataTypeAccesses',
43
- 'customSettingAccesses',
44
- 'recordTypeVisibilities',
45
- ];
46
- const filterFunctions = {
47
- objectPermissions: (props, packageXml) => props.filter((item) => packageXml.get('CustomObject')?.includes(item.object)),
48
- fieldPermissions: (props, packageXml) => props.filter((item) => packageXml.get('CustomField')?.includes((0, exports.fieldCorrections)(item.field))),
49
- layoutAssignments: (props, packageXml) => props.filter((item) => packageXml.get('Layout')?.includes(item.layout)),
50
- tabVisibilities: (props, packageXml) => props.filter((item) => packageXml.get('CustomTab')?.includes(item.tab)),
51
- applicationVisibilities: (props, packageXml) => props.filter((item) => packageXml.get('Application')?.includes(item.application)),
52
- classAccesses: (props, packageXml) => props.filter((item) => packageXml.get('ApexClass')?.includes(item.apexClass)),
53
- customPermissions: (props, packageXml) => props.filter((item) => packageXml.get('CustomPermission')?.includes(item.name)),
54
- pageAccesses: (props, packageXml) => props.filter((item) => packageXml.get('ApexPage')?.includes(item.apexPage)),
55
- externalDataSourceAccesses: (props, packageXml) => props.filter((item) => packageXml.get('ExternalDataSource')?.includes(item.externalDataSource)),
56
- recordTypeVisibilities: (props, packageXml) => props.filter((item) => packageXml.get('RecordType')?.includes(item.recordType)),
57
- customSettingAccesses: (props, packageXml) => props.filter((item) => allMembers(packageXml).includes(item.name)),
58
- customMetadataTypeAccesses: (props, packageXml) => props.filter((item) => allMembers(packageXml).includes(item.name)),
59
- };
60
- const allMembers = (packageXml) => Array.from(packageXml.values()).flat();
61
- // github.com/forcedotcom/cli/issues/2278
62
- // Activity Object is polymorphic (Task and Event)
63
- // package.xml will display them as 'Activity'
64
- // profile.fieldPermissions will display them with the more specific 'Task' or 'Event'
65
- const fieldCorrections = (fieldName) => fieldName.replace(/^Event\./, 'Activity.').replace(/^Task\./, 'Activity.');
66
- exports.fieldCorrections = fieldCorrections;
67
- /**
68
- * @param profileString: raw xml read from the file
69
- * @returns CorrectedProfile (json representation of the profile)
70
- */
71
- const profileStringToProfile = (profileString) => {
72
- const parser = new fast_xml_parser_1.XMLParser({
73
- ignoreAttributes: true,
74
- parseTagValue: false,
75
- parseAttributeValue: false,
76
- cdataPropName: '__cdata',
77
- ignoreDeclaration: true,
78
- numberParseOptions: { leadingZeros: false, hex: false },
79
- isArray: (name) => rewriteProps.includes(name),
80
- });
81
- return parser.parse(profileString).Profile;
82
- };
83
- exports.profileStringToProfile = profileStringToProfile;
84
- /** pass in an object that has the Profile props at the top level.
85
- * This function will add the outer wrapper `Profile` and convert the result to xml
86
- * */
87
- const profileObjectToString = (profileObject) => {
88
- const builder = new fast_xml_parser_1.XMLBuilder({
89
- format: true,
90
- indentBy: ' ',
91
- ignoreAttributes: false,
92
- cdataPropName: '__cdata',
93
- processEntities: false,
94
- attributeNamePrefix: '@@@',
95
- });
96
- return String(builder.build({
97
- '?xml': {
98
- '@@@version': '1.0',
99
- '@@@encoding': 'UTF-8',
100
- },
101
- Profile: { ...profileObject, '@@@xmlns': 'http://soap.sforce.com/2006/04/metadata' },
102
- }));
103
- };
104
- exports.profileObjectToString = profileObjectToString;
105
- /** it's easier to do lookups by Metadata Type on a Map */
106
- const manifestTypesToMap = (original) => new Map(original.map((item) => [item.name, item.members]));
107
- exports.manifestTypesToMap = manifestTypesToMap;
108
- //# sourceMappingURL=profileRewriter.js.map