@salesforce/source-tracking 2.1.2 → 2.2.2

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.
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.populateTypesAndNames = void 0;
4
+ /*
5
+ * Copyright (c) 2020, salesforce.com, inc.
6
+ * All rights reserved.
7
+ * Licensed under the BSD 3-Clause license.
8
+ * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
9
+ */
10
+ const core_1 = require("@salesforce/core");
11
+ const ts_types_1 = require("@salesforce/ts-types");
12
+ const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
13
+ const guards_1 = require("./guards");
14
+ const functions_1 = require("./functions");
15
+ const logger = core_1.Logger.childFromRoot('SourceTracking.PopulateTypesAndNames');
16
+ /**
17
+ * uses SDR to translate remote metadata records into local file paths (which only typically have the filename).
18
+ *
19
+ * @input elements: ChangeResult[]
20
+ * @input projectPath
21
+ * @input forceIgnore: ForceIgnore. If provided, result will indicate whether the file is ignored
22
+ * @input excludeUnresolvable: boolean Filter out components where you can't get the name and type (that is, it's probably not a valid source component)
23
+ * @input resolveDeleted: constructs a virtualTree instead of the actual filesystem--useful when the files no longer exist
24
+ */
25
+ const populateTypesAndNames = ({ elements, projectPath, forceIgnore, excludeUnresolvable = false, resolveDeleted = false, }) => {
26
+ if (elements.length === 0) {
27
+ return [];
28
+ }
29
+ logger.debug(`populateTypesAndNames for ${elements.length} change elements`);
30
+ const filenames = elements.flatMap((element) => element.filenames).filter(ts_types_1.isString);
31
+ // component set generated from the filenames on all local changes
32
+ const resolver = new source_deploy_retrieve_1.MetadataResolver(undefined, resolveDeleted ? source_deploy_retrieve_1.VirtualTreeContainer.fromFilePaths(filenames) : undefined, !!forceIgnore);
33
+ const sourceComponents = filenames
34
+ .flatMap((filename) => {
35
+ try {
36
+ return resolver.getComponentsFromPath(filename);
37
+ }
38
+ catch (e) {
39
+ logger.warn(`unable to resolve ${filename}`);
40
+ return undefined;
41
+ }
42
+ })
43
+ .filter(guards_1.sourceComponentGuard);
44
+ logger.debug(` matching SourceComponents have ${sourceComponents.length} items from local`);
45
+ // make it simpler to find things later
46
+ const elementMap = new Map();
47
+ elements.map((element) => {
48
+ element.filenames?.map((filename) => {
49
+ elementMap.set((0, functions_1.ensureRelative)(filename, projectPath), element);
50
+ });
51
+ });
52
+ // iterates the local components and sets their filenames
53
+ sourceComponents.map((matchingComponent) => {
54
+ if (matchingComponent?.fullName && matchingComponent?.type.name) {
55
+ const filenamesFromMatchingComponent = [matchingComponent.xml, ...matchingComponent.walkContent()];
56
+ const ignored = filenamesFromMatchingComponent
57
+ .filter(ts_types_1.isString)
58
+ .filter((f) => !(0, functions_1.isLwcLocalOnlyTest)(f))
59
+ .some((f) => forceIgnore?.denies(f));
60
+ filenamesFromMatchingComponent.map((filename) => {
61
+ if (filename && elementMap.has(filename)) {
62
+ // add the type/name from the componentSet onto the element
63
+ elementMap.set(filename, {
64
+ origin: 'remote',
65
+ ...elementMap.get(filename),
66
+ type: matchingComponent.type.name,
67
+ name: matchingComponent.fullName,
68
+ ignored,
69
+ });
70
+ }
71
+ });
72
+ }
73
+ });
74
+ return excludeUnresolvable
75
+ ? Array.from(new Set(elementMap.values())).filter((changeResult) => changeResult.name && changeResult.type)
76
+ : Array.from(new Set(elementMap.values()));
77
+ };
78
+ exports.populateTypesAndNames = populateTypesAndNames;
79
+ //# sourceMappingURL=populateTypesAndNames.js.map
@@ -128,11 +128,6 @@ export declare class RemoteSourceTrackingService extends ConfigFile<RemoteSource
128
128
  * @param pollingTimeout maximum amount of time in seconds to poll for SourceMembers
129
129
  */
130
130
  pollForSourceTracking(expectedMembers: RemoteSyncInput[]): Promise<void>;
131
- /**
132
- * Filter out known source tracking issues
133
- * This prevents the polling from waiting on things that may never return
134
- */
135
- private calculateExpectedSourceMembers;
136
131
  private calculateTimeout;
137
132
  private querySourceMembersFrom;
138
133
  private querySourceMembersTo;
@@ -7,23 +7,22 @@
7
7
  */
8
8
  /* eslint-disable no-underscore-dangle */
9
9
  /* eslint-disable @typescript-eslint/member-ordering */
10
- var _a;
11
10
  Object.defineProperty(exports, "__esModule", { value: true });
12
11
  exports.remoteChangeElementToChangeResult = exports.RemoteSourceTrackingService = void 0;
13
12
  const path = require("path");
14
13
  const fs = require("fs");
15
14
  const ts_retry_promise_1 = require("ts-retry-promise");
16
15
  const core_1 = require("@salesforce/core");
17
- const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
18
16
  const kit_1 = require("@salesforce/kit");
19
17
  const metadataKeys_1 = require("./metadataKeys");
20
18
  const functions_1 = require("./functions");
19
+ const expectedSourceMembers_1 = require("./expectedSourceMembers");
21
20
  /*
22
21
  * after some results have returned, how many times should we poll for missing sourcemembers
23
22
  * even when there is a longer timeout remaining (because the deployment is very large)
24
23
  */
25
24
  const POLLING_DELAY_MS = 1000;
26
- const CONSECUTIVE_EMPTY_POLLING_RESULT_LIMIT = ((_a = kit_1.env.getNumber('SFDX_SOURCE_MEMBER_POLLING_TIMEOUT')) !== null && _a !== void 0 ? _a : 120) / kit_1.Duration.milliseconds(POLLING_DELAY_MS).seconds;
25
+ const CONSECUTIVE_EMPTY_POLLING_RESULT_LIMIT = (kit_1.env.getNumber('SFDX_SOURCE_MEMBER_POLLING_TIMEOUT') ?? 120) / kit_1.Duration.milliseconds(POLLING_DELAY_MS).seconds;
27
26
  /**
28
27
  * This service handles source tracking of metadata between a local project and an org.
29
28
  * Source tracking state is persisted to .sfdx/orgs/<orgId>/maxRevision.json.
@@ -264,11 +263,10 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
264
263
  }
265
264
  let serverMaxRevisionCounter = this.getServerMaxRevision();
266
265
  sourceMembers.forEach((change) => {
267
- var _a;
268
266
  // try accessing the sourceMembers object at the index of the change's name
269
267
  // if it exists, we'll update the fields - if it doesn't, we'll create and insert it
270
268
  const key = (0, functions_1.getMetadataKey)(change.MemberType, change.MemberName);
271
- const sourceMember = (_a = this.getSourceMember(key)) !== null && _a !== void 0 ? _a : {
269
+ const sourceMember = this.getSourceMember(key) ?? {
272
270
  serverRevisionCounter: change.RevisionCounter,
273
271
  lastRetrievedFromServer: null,
274
272
  memberType: change.MemberType,
@@ -282,8 +280,8 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
282
280
  sourceMember.serverRevisionCounter = change.RevisionCounter;
283
281
  sourceMember.isNameObsolete = change.IsNameObsolete;
284
282
  }
285
- // We are not yet tracking it so we'll insert a new record
286
283
  else if (!quiet) {
284
+ // We are not yet tracking it so we'll insert a new record
287
285
  this.logger.debug(`Inserting ${key} with RevisionCounter: ${change.RevisionCounter}${sync ? ' and syncing' : ''}`);
288
286
  }
289
287
  // If we are syncing changes then we need to update the lastRetrievedFromServer field to
@@ -349,7 +347,7 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
349
347
  this.logger.warn('Not polling for SourceMembers since SFDX_DISABLE_SOURCE_MEMBER_POLLING = true.');
350
348
  return;
351
349
  }
352
- const outstandingSourceMembers = this.calculateExpectedSourceMembers(expectedMembers);
350
+ const outstandingSourceMembers = (0, expectedSourceMembers_1.calculateExpectedSourceMembers)(expectedMembers);
353
351
  if (expectedMembers.length === 0 || outstandingSourceMembers.size === 0) {
354
352
  // Don't bother polling if we're not matching SourceMembers
355
353
  return;
@@ -431,78 +429,8 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
431
429
  });
432
430
  }
433
431
  }
434
- /**
435
- * Filter out known source tracking issues
436
- * This prevents the polling from waiting on things that may never return
437
- */
438
- // eslint-disable-next-line class-methods-use-this
439
- calculateExpectedSourceMembers(expectedMembers) {
440
- const outstandingSourceMembers = new Map();
441
- expectedMembers
442
- .filter(
443
- // eslint-disable-next-line complexity
444
- (fileResponse) => {
445
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
446
- // unchanged files will never be in the sourceMembers. Not really sure why SDR returns them.
447
- return fileResponse.state !== source_deploy_retrieve_1.ComponentStatus.Unchanged &&
448
- // if a listView is the only change inside an object, the object won't have a sourceMember change. We won't wait for those to be found
449
- // we don't know which email folder type might be there, so don't require either
450
- // Portal doesn't support source tracking, according to the coverage report
451
- ![
452
- 'CustomObject',
453
- 'EmailFolder',
454
- 'EmailTemplateFolder',
455
- 'StandardValueSet',
456
- 'Portal',
457
- 'StandardValueSetTranslation',
458
- 'SharingRules',
459
- 'SharingCriteriaRule',
460
- 'GlobalValueSetTranslation',
461
- 'AssignmentRules',
462
- ].includes(fileResponse.type) &&
463
- // don't wait for standard fields on standard objects
464
- !(fileResponse.type === 'CustomField' && !((_a = fileResponse.filePath) === null || _a === void 0 ? void 0 : _a.includes('__c'))) &&
465
- // deleted fields
466
- !(fileResponse.type === 'CustomField' && !((_b = fileResponse.filePath) === null || _b === void 0 ? void 0 : _b.includes('_del__c'))) &&
467
- // built-in report type ReportType__screen_flows_prebuilt_crt
468
- !(fileResponse.type === 'ReportType' && ((_c = fileResponse.filePath) === null || _c === void 0 ? void 0 : _c.includes('screen_flows_prebuilt_crt'))) &&
469
- // they're settings to mdapi, and FooSettings in sourceMembers
470
- !fileResponse.type.includes('Settings') &&
471
- // mdapi encodes these, sourceMembers don't have encoding
472
- !((fileResponse.type === 'Layout' ||
473
- fileResponse.type === 'BusinessProcess' ||
474
- fileResponse.type === 'Profile' ||
475
- fileResponse.type === 'HomePageComponent' ||
476
- fileResponse.type === 'HomePageLayout') &&
477
- ((_d = fileResponse.filePath) === null || _d === void 0 ? void 0 : _d.includes('%'))) &&
478
- // namespaced labels and CMDT don't resolve correctly
479
- !(['CustomLabels', 'CustomMetadata'].includes(fileResponse.type) && ((_e = fileResponse.filePath) === null || _e === void 0 ? void 0 : _e.includes('__'))) &&
480
- // don't wait on workflow children
481
- !fileResponse.type.startsWith('Workflow') &&
482
- // aura xml aren't tracked as SourceMembers
483
- !((_f = fileResponse.filePath) === null || _f === void 0 ? void 0 : _f.endsWith('.cmp-meta.xml')) &&
484
- !((_g = fileResponse.filePath) === null || _g === void 0 ? void 0 : _g.endsWith('.tokens-meta.xml')) &&
485
- !((_h = fileResponse.filePath) === null || _h === void 0 ? void 0 : _h.endsWith('.evt-meta.xml')) &&
486
- !((_j = fileResponse.filePath) === null || _j === void 0 ? void 0 : _j.endsWith('.app-meta.xml')) &&
487
- !((_k = fileResponse.filePath) === null || _k === void 0 ? void 0 : _k.endsWith('.intf-meta.xml'));
488
- })
489
- .map((member) => {
490
- (0, metadataKeys_1.getMetadataKeyFromFileResponse)(member)
491
- // remove some individual members known to not work with tracking even when their type does
492
- .filter((key) =>
493
- // CustomObject could have been re-added by the key generator from one of its fields
494
- !key.startsWith('CustomObject') &&
495
- key !== 'Profile__Standard' &&
496
- key !== 'CustomTab__standard-home' &&
497
- key !== 'AssignmentRules__Case' &&
498
- key !== 'ListView__CollaborationGroup.All_ChatterGroups')
499
- .map((key) => outstandingSourceMembers.set(key, member));
500
- });
501
- return outstandingSourceMembers;
502
- }
503
432
  calculateTimeout(memberCount) {
504
- var _a;
505
- const overriddenTimeout = (_a = kit_1.env.getNumber('SFDX_SOURCE_MEMBER_POLLING_TIMEOUT', 0)) !== null && _a !== void 0 ? _a : 0;
433
+ const overriddenTimeout = kit_1.env.getNumber('SFDX_SOURCE_MEMBER_POLLING_TIMEOUT', 0) ?? 0;
506
434
  if (overriddenTimeout > 0) {
507
435
  this.logger.debug(`Overriding SourceMember polling timeout to ${overriddenTimeout}`);
508
436
  return kit_1.Duration.seconds(overriddenTimeout);
@@ -1,4 +1,5 @@
1
1
  import { FileResponse, SourceComponent } from '@salesforce/source-deploy-retrieve';
2
+ import { SfError } from '@salesforce/core';
2
3
  export interface ChangeOptions {
3
4
  origin: 'local' | 'remote';
4
5
  state: 'add' | 'delete' | 'modify' | 'nondelete';
@@ -40,9 +41,16 @@ export declare type SourceMember = {
40
41
  RevisionCounter: number;
41
42
  ignored?: boolean;
42
43
  };
43
- export interface ConflictError {
44
- message: string;
45
- name: 'conflict';
46
- conflicts: ChangeResult[];
44
+ export interface ConflictResponse {
45
+ state: 'Conflict';
46
+ fullName: string;
47
+ type: string;
48
+ filePath: string;
49
+ }
50
+ export interface SourceConflictError extends SfError {
51
+ name: 'SourceConflictError';
52
+ data: ConflictResponse[];
53
+ }
54
+ export declare class SourceConflictError extends SfError implements SourceConflictError {
47
55
  }
48
56
  export declare type ChangeOptionType = ChangeResult | SourceComponent | string;
@@ -6,4 +6,9 @@
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.SourceConflictError = void 0;
10
+ const core_1 = require("@salesforce/core");
11
+ class SourceConflictError extends core_1.SfError {
12
+ }
13
+ exports.SourceConflictError = SourceConflictError;
9
14
  //# sourceMappingURL=types.js.map
@@ -1,11 +1,28 @@
1
1
  import { Org, SfProject } from '@salesforce/core';
2
2
  import { AsyncCreatable } from '@salesforce/kit';
3
- import { ComponentSet, SourceComponent, FileResponse } from '@salesforce/source-deploy-retrieve';
4
- import { RemoteSyncInput, StatusOutputRow, ChangeOptions, ChangeResult, ChangeOptionType, LocalUpdateOptions } from './shared/types';
3
+ import { ComponentSet, SourceComponent, FileResponse, DeployResult, RetrieveResult } from '@salesforce/source-deploy-retrieve';
4
+ import { RemoteSyncInput, StatusOutputRow, ChangeOptions, ChangeResult, LocalUpdateOptions } from './shared/types';
5
5
  export interface SourceTrackingOptions {
6
6
  org: Org;
7
7
  project: SfProject;
8
+ /** listen for the SDR scoped<Pre|Post><Deploy|Retrieve> events
9
+ * `pre` events will check for conflicts and throw if there are any (use ignoreConflicts: true to disable)
10
+ * `post` events will update tracking files with the results of the deploy/retrieve
11
+ */
12
+ subscribeSDREvents?: boolean;
13
+ /** don't check for conflicts when responding to SDR events.
14
+ * This property has no effect unless you also set subscribeSDREvents to true.
15
+ */
16
+ ignoreConflicts?: boolean;
17
+ /** SourceTracking is caching local file statuses.
18
+ * If you're using STL as part of a long running process (ex: vscode extensions), set this to false
19
+ */
20
+ ignoreLocalCache?: boolean;
8
21
  }
22
+ declare type RemoteChangesResults = {
23
+ componentSetFromNonDeletes: ComponentSet;
24
+ fileResponsesFromDelete: FileResponse[];
25
+ };
9
26
  /**
10
27
  * Manages source tracking files (remote and local)
11
28
  *
@@ -18,16 +35,21 @@ export declare class SourceTracking extends AsyncCreatable {
18
35
  private projectPath;
19
36
  private packagesDirs;
20
37
  private logger;
21
- private registry;
22
38
  private localRepo;
23
39
  private remoteSourceTrackingService;
24
40
  private forceIgnore;
25
41
  private hasSfdxTrackingFiles;
42
+ private ignoreConflicts;
43
+ private subscribeSDREvents;
44
+ private ignoreLocalCache;
45
+ private orgId;
26
46
  constructor(options: SourceTrackingOptions);
27
47
  init(): Promise<void>;
28
48
  /**
29
49
  *
30
- * @param byPackageDir if true, returns one ComponentSet for each packageDir with changes
50
+ * @param byPackageDir if true, returns a ComponentSet for each packageDir that has any changes
51
+ * * if false, returns an array containing one ComponentSet with all changes
52
+ * * if not specified, this method will follow what sfdx-project.json says
31
53
  * @returns ComponentSet[]
32
54
  */
33
55
  localChangesAsComponentSet(byPackageDir?: boolean): Promise<ComponentSet[]>;
@@ -50,7 +72,48 @@ export declare class SourceTracking extends AsyncCreatable {
50
72
  * @returns local and remote changed metadata
51
73
  *
52
74
  */
53
- getChanges<T extends ChangeOptionType>(options?: ChangeOptions): Promise<T[]>;
75
+ getChanges(options: ChangeOptions & {
76
+ format: 'string';
77
+ }): Promise<string[]>;
78
+ getChanges(options: ChangeOptions & {
79
+ format: 'SourceComponent';
80
+ }): Promise<SourceComponent[]>;
81
+ getChanges(options: ChangeOptions & {
82
+ format: 'ChangeResult';
83
+ }): Promise<ChangeResult[]>;
84
+ /**
85
+ * @deprecated omit the type parameter <string>.
86
+ */
87
+ getChanges<T extends string>(options: ChangeOptions & {
88
+ format: 'string';
89
+ }): Promise<T[]>;
90
+ /**
91
+ * @deprecated omit the type parameter <SourceComponent>.
92
+ */
93
+ getChanges<T extends SourceComponent>(options: ChangeOptions & {
94
+ format: 'SourceComponent';
95
+ }): Promise<T[]>;
96
+ /**
97
+ * @deprecated omit the type parameter <ChangeResult>.
98
+ */
99
+ getChanges<T extends ChangeResult>(options: ChangeOptions & {
100
+ format: 'ChangeResult';
101
+ }): Promise<T[]>;
102
+ getChanges(options: ChangeOptions & {
103
+ format: 'ChangeResultWithPaths';
104
+ }): Promise<Array<ChangeResult & {
105
+ filename: string[];
106
+ }>>;
107
+ /**
108
+ *
109
+ * Convenience method to reduce duplicated steps required to do a fka pull
110
+ * It's full of side effects: retrieving remote deletes, deleting those files locall, and then updating tracking files
111
+ * Most bizarrely, it then returns a ComponentSet of the remote nonDeletes and the FileResponses from the delete
112
+ *
113
+ * @returns the ComponentSet for what you would retrieve now that the deletes are done, and optionally, a FileResponses array for the deleted files
114
+ */
115
+ maybeApplyRemoteDeletesToLocal(returnDeleteFileResponses: true): Promise<RemoteChangesResults>;
116
+ maybeApplyRemoteDeletesToLocal(returnDeleteFileResponses?: false): Promise<ComponentSet>;
54
117
  /**
55
118
  *
56
119
  * returns immediately if there are no changesToDelete
@@ -69,9 +132,10 @@ export declare class SourceTracking extends AsyncCreatable {
69
132
  * Optionall skip polling for the SourceMembers to exist on the server and be updated in local files
70
133
  */
71
134
  updateRemoteTracking(fileResponses: RemoteSyncInput[], skipPolling?: boolean): Promise<void>;
135
+ reReadLocalTrackingCache(): Promise<void>;
72
136
  /**
73
137
  * If the local tracking shadowRepo doesn't exist, it will be created.
74
- * Does nothing if it already exists.
138
+ * Does nothing if it already exists, unless you've instantiate SourceTracking to not cache local status, in which case it'll re-read your files
75
139
  * Useful before parallel operations
76
140
  */
77
141
  ensureLocalTracking(): Promise<void>;
@@ -103,22 +167,28 @@ export declare class SourceTracking extends AsyncCreatable {
103
167
  */
104
168
  getConflicts(): Promise<ChangeResult[]>;
105
169
  /**
106
- * uses SDR to translate remote metadata records into local file paths (which only typically have the filename).
170
+ * handles both remote and local tracking
107
171
  *
108
- * @input elements: ChangeResult[]
109
- * @input excludeUnresolvable: boolean Filter out components where you can't get the name and type (that is, it's probably not a valid source component)
110
- * @input resolveDeleted: constructs a virtualTree instead of the actual filesystem--useful when the files no longer exist
111
- * @input useFsForceIgnore: (default behavior) use forceIgnore from the filesystem. If false, uses the base forceIgnore from SDR
172
+ * @param result FileResponse[]
112
173
  */
113
- private populateTypesAndNames;
114
- private getLocalStatusRows;
115
- private registrySupportsType;
174
+ updateTrackingFromDeploy(deployResult: DeployResult): Promise<void>;
116
175
  /**
117
- * uses SDR to translate remote metadata records into local file paths
176
+ * handles both remote and local tracking
177
+ *
178
+ * @param result FileResponse[]
118
179
  */
119
- private populateFilePaths;
120
- private ensureRelative;
180
+ updateTrackingFromRetrieve(retrieveResult: RetrieveResult): Promise<void>;
181
+ /**
182
+ * If you've already got an instance of STL, but need to change the conflicts setting
183
+ * normally you set this on instantiation
184
+ *
185
+ * @param value true/false
186
+ */
187
+ setIgnoreConflicts(value: boolean): void;
188
+ private maybeSubscribeLifecycleEvents;
189
+ private getLocalStatusRows;
121
190
  private getLocalChangesAsFilenames;
122
191
  private localChangesToOutputRow;
123
192
  private remoteChangesToOutputRows;
124
193
  }
194
+ export {};