@salesforce/source-tracking 2.1.1 → 2.2.1

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,80 @@
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
+ var _a;
49
+ (_a = element.filenames) === null || _a === void 0 ? void 0 : _a.map((filename) => {
50
+ elementMap.set((0, functions_1.ensureRelative)(filename, projectPath), element);
51
+ });
52
+ });
53
+ // iterates the local components and sets their filenames
54
+ sourceComponents.map((matchingComponent) => {
55
+ if ((matchingComponent === null || matchingComponent === void 0 ? void 0 : matchingComponent.fullName) && (matchingComponent === null || matchingComponent === void 0 ? void 0 : matchingComponent.type.name)) {
56
+ const filenamesFromMatchingComponent = [matchingComponent.xml, ...matchingComponent.walkContent()];
57
+ const ignored = filenamesFromMatchingComponent
58
+ .filter(ts_types_1.isString)
59
+ .filter((f) => !(0, functions_1.isLwcLocalOnlyTest)(f))
60
+ .some((f) => forceIgnore === null || forceIgnore === void 0 ? void 0 : forceIgnore.denies(f));
61
+ filenamesFromMatchingComponent.map((filename) => {
62
+ if (filename && elementMap.has(filename)) {
63
+ // add the type/name from the componentSet onto the element
64
+ elementMap.set(filename, {
65
+ origin: 'remote',
66
+ ...elementMap.get(filename),
67
+ type: matchingComponent.type.name,
68
+ name: matchingComponent.fullName,
69
+ ignored,
70
+ });
71
+ }
72
+ });
73
+ }
74
+ });
75
+ return excludeUnresolvable
76
+ ? Array.from(new Set(elementMap.values())).filter((changeResult) => changeResult.name && changeResult.type)
77
+ : Array.from(new Set(elementMap.values()));
78
+ };
79
+ exports.populateTypesAndNames = populateTypesAndNames;
80
+ //# 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;
@@ -14,10 +14,10 @@ const path = require("path");
14
14
  const fs = require("fs");
15
15
  const ts_retry_promise_1 = require("ts-retry-promise");
16
16
  const core_1 = require("@salesforce/core");
17
- const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
18
17
  const kit_1 = require("@salesforce/kit");
19
18
  const metadataKeys_1 = require("./metadataKeys");
20
19
  const functions_1 = require("./functions");
20
+ const expectedSourceMembers_1 = require("./expectedSourceMembers");
21
21
  /*
22
22
  * after some results have returned, how many times should we poll for missing sourcemembers
23
23
  * even when there is a longer timeout remaining (because the deployment is very large)
@@ -282,8 +282,8 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
282
282
  sourceMember.serverRevisionCounter = change.RevisionCounter;
283
283
  sourceMember.isNameObsolete = change.IsNameObsolete;
284
284
  }
285
- // We are not yet tracking it so we'll insert a new record
286
285
  else if (!quiet) {
286
+ // We are not yet tracking it so we'll insert a new record
287
287
  this.logger.debug(`Inserting ${key} with RevisionCounter: ${change.RevisionCounter}${sync ? ' and syncing' : ''}`);
288
288
  }
289
289
  // If we are syncing changes then we need to update the lastRetrievedFromServer field to
@@ -349,7 +349,7 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
349
349
  this.logger.warn('Not polling for SourceMembers since SFDX_DISABLE_SOURCE_MEMBER_POLLING = true.');
350
350
  return;
351
351
  }
352
- const outstandingSourceMembers = this.calculateExpectedSourceMembers(expectedMembers);
352
+ const outstandingSourceMembers = (0, expectedSourceMembers_1.calculateExpectedSourceMembers)(expectedMembers);
353
353
  if (expectedMembers.length === 0 || outstandingSourceMembers.size === 0) {
354
354
  // Don't bother polling if we're not matching SourceMembers
355
355
  return;
@@ -431,75 +431,6 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
431
431
  });
432
432
  }
433
433
  }
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
434
  calculateTimeout(memberCount) {
504
435
  var _a;
505
436
  const overriddenTimeout = (_a = kit_1.env.getNumber('SFDX_SOURCE_MEMBER_POLLING_TIMEOUT', 0)) !== null && _a !== void 0 ? _a : 0;
@@ -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,10 +1,23 @@
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
  }
9
22
  /**
10
23
  * Manages source tracking files (remote and local)
@@ -18,16 +31,21 @@ export declare class SourceTracking extends AsyncCreatable {
18
31
  private projectPath;
19
32
  private packagesDirs;
20
33
  private logger;
21
- private registry;
22
34
  private localRepo;
23
35
  private remoteSourceTrackingService;
24
36
  private forceIgnore;
25
37
  private hasSfdxTrackingFiles;
38
+ private ignoreConflicts;
39
+ private subscribeSDREvents;
40
+ private ignoreLocalCache;
41
+ private orgId;
26
42
  constructor(options: SourceTrackingOptions);
27
43
  init(): Promise<void>;
28
44
  /**
29
45
  *
30
- * @param byPackageDir if true, returns one ComponentSet for each packageDir with changes
46
+ * @param byPackageDir if true, returns a ComponentSet for each packageDir that has any changes
47
+ * * if false, returns an array containing one ComponentSet with all changes
48
+ * * if not specified, this method will follow what sfdx-project.json says
31
49
  * @returns ComponentSet[]
32
50
  */
33
51
  localChangesAsComponentSet(byPackageDir?: boolean): Promise<ComponentSet[]>;
@@ -50,7 +68,47 @@ export declare class SourceTracking extends AsyncCreatable {
50
68
  * @returns local and remote changed metadata
51
69
  *
52
70
  */
53
- getChanges<T extends ChangeOptionType>(options?: ChangeOptions): Promise<T[]>;
71
+ getChanges(options: ChangeOptions & {
72
+ format: 'string';
73
+ }): Promise<string[]>;
74
+ getChanges(options: ChangeOptions & {
75
+ format: 'SourceComponent';
76
+ }): Promise<SourceComponent[]>;
77
+ getChanges(options: ChangeOptions & {
78
+ format: 'ChangeResult';
79
+ }): Promise<ChangeResult[]>;
80
+ /**
81
+ * @deprecated omit the type parameter <string>.
82
+ */
83
+ getChanges<T extends string>(options: ChangeOptions & {
84
+ format: 'string';
85
+ }): Promise<T[]>;
86
+ /**
87
+ * @deprecated omit the type parameter <SourceComponent>.
88
+ */
89
+ getChanges<T extends SourceComponent>(options: ChangeOptions & {
90
+ format: 'SourceComponent';
91
+ }): Promise<T[]>;
92
+ /**
93
+ * @deprecated omit the type parameter <ChangeResult>.
94
+ */
95
+ getChanges<T extends ChangeResult>(options: ChangeOptions & {
96
+ format: 'ChangeResult';
97
+ }): Promise<T[]>;
98
+ getChanges(options: ChangeOptions & {
99
+ format: 'ChangeResultWithPaths';
100
+ }): Promise<Array<ChangeResult & {
101
+ filename: string[];
102
+ }>>;
103
+ /**
104
+ *
105
+ * Convenience method to reduce duplicated steps required to do a fka pull
106
+ * It's full of side effects: retrieving remote deletes, deleting those files locall, and then updating tracking files
107
+ * Most bizarrely, it then returns a ComponentSet of the remote nonDeletes.
108
+ *
109
+ * @returns the ComponentSet for what you would retrieve now that the deletes are done
110
+ */
111
+ maybeApplyRemoteDeletesToLocal(): Promise<ComponentSet>;
54
112
  /**
55
113
  *
56
114
  * returns immediately if there are no changesToDelete
@@ -69,9 +127,10 @@ export declare class SourceTracking extends AsyncCreatable {
69
127
  * Optionall skip polling for the SourceMembers to exist on the server and be updated in local files
70
128
  */
71
129
  updateRemoteTracking(fileResponses: RemoteSyncInput[], skipPolling?: boolean): Promise<void>;
130
+ reReadLocalTrackingCache(): Promise<void>;
72
131
  /**
73
132
  * If the local tracking shadowRepo doesn't exist, it will be created.
74
- * Does nothing if it already exists.
133
+ * 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
134
  * Useful before parallel operations
76
135
  */
77
136
  ensureLocalTracking(): Promise<void>;
@@ -103,21 +162,26 @@ export declare class SourceTracking extends AsyncCreatable {
103
162
  */
104
163
  getConflicts(): Promise<ChangeResult[]>;
105
164
  /**
106
- * uses SDR to translate remote metadata records into local file paths (which only typically have the filename).
165
+ * handles both remote and local tracking
107
166
  *
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
167
+ * @param result FileResponse[]
112
168
  */
113
- private populateTypesAndNames;
114
- private getLocalStatusRows;
115
- private registrySupportsType;
169
+ updateTrackingFromDeploy(deployResult: DeployResult): Promise<void>;
116
170
  /**
117
- * uses SDR to translate remote metadata records into local file paths
171
+ * handles both remote and local tracking
172
+ *
173
+ * @param result FileResponse[]
118
174
  */
119
- private populateFilePaths;
120
- private ensureRelative;
175
+ updateTrackingFromRetrieve(retrieveResult: RetrieveResult): Promise<void>;
176
+ /**
177
+ * If you've already got an instance of STL, but need to change the conflicts setting
178
+ * normally you set this on instantiation
179
+ *
180
+ * @param value true/false
181
+ */
182
+ setIgnoreConflicts(value: boolean): void;
183
+ private maybeSubscribeLifecycleEvents;
184
+ private getLocalStatusRows;
121
185
  private getLocalChangesAsFilenames;
122
186
  private localChangesToOutputRow;
123
187
  private remoteChangesToOutputRows;