@salesforce/source-tracking 0.5.2 → 1.1.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.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,40 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [1.1.0](https://github.com/forcedotcom/source-tracking/compare/v1.0.2...v1.1.0) (2022-02-16)
6
+
7
+ ### Features
8
+
9
+ - smarter polling ([3d5bb05](https://github.com/forcedotcom/source-tracking/commit/3d5bb05ccf05127304037ec6085660be5da8fe24))
10
+
11
+ ### Bug Fixes
12
+
13
+ - handle lwc in a pkgDir of the same name ([621d8cf](https://github.com/forcedotcom/source-tracking/commit/621d8cfc00f2eb4ded160802b2076e140d0a2d06))
14
+ - increase max-fetch default ([9854ed8](https://github.com/forcedotcom/source-tracking/commit/9854ed8677c13d0b2d818355660ab71c144e6a3e))
15
+ - lastIndexOf handles foo/lwc/foo ([8632ee6](https://github.com/forcedotcom/source-tracking/commit/8632ee61f88faf32024e99f1c5590f6b134a3b08))
16
+ - smarter polling and excluded files ([6df02a7](https://github.com/forcedotcom/source-tracking/commit/6df02a716e238e38b699b98f638ef42ed8cedeca))
17
+
18
+ ### [1.0.2](https://github.com/forcedotcom/source-tracking/compare/v1.0.1...v1.0.2) (2022-01-25)
19
+
20
+ ### Bug Fixes
21
+
22
+ - handle gitignore outside pkgDirs ([23a65c8](https://github.com/forcedotcom/source-tracking/commit/23a65c89c8b55525b5d8efe88195d734d337d82a))
23
+
24
+ ### [1.0.1](https://github.com/forcedotcom/source-tracking/compare/v1.0.0...v1.0.1) (2022-01-25)
25
+
26
+ ### Bug Fixes
27
+
28
+ - emailTempalteFolder via aliased types ([f4c88f9](https://github.com/forcedotcom/source-tracking/commit/f4c88f9b59ad6d061933bfd3f6827e44a59b0e80))
29
+
30
+ ## [1.0.0](https://github.com/forcedotcom/source-tracking/compare/v0.5.2...v1.0.0) (2022-01-20)
31
+
32
+ ### Bug Fixes
33
+
34
+ - handle element count errors ([8817329](https://github.com/forcedotcom/source-tracking/commit/8817329f8198ce701aba22c7a4476ef31a4d73a4))
35
+ - lightning EmailTemplateFolder ([554c766](https://github.com/forcedotcom/source-tracking/commit/554c76676a85c7a4b673879116912cde51dd1498))
36
+ - remove emailtf attempt ([262839d](https://github.com/forcedotcom/source-tracking/commit/262839dc025c7094d8868d3ae9d769dd39ad5324))
37
+ - sourceMember excepton for nondecomposed children ([05db59e](https://github.com/forcedotcom/source-tracking/commit/05db59e7a6e070224a45f5fbf08013565c9f2131))
38
+
5
39
  ### [0.5.2](https://github.com/forcedotcom/source-tracking/compare/v0.5.1...v0.5.2) (2022-01-05)
6
40
 
7
41
  ### Features
@@ -50,6 +50,7 @@ class ShadowRepo {
50
50
  this.logger.debug('initializing git repo');
51
51
  await this.gitInit();
52
52
  }
53
+ await this.locateIgnoreFiles();
53
54
  }
54
55
  /**
55
56
  * Initialize a new source tracking shadow repo. Think of git init
@@ -58,7 +59,6 @@ class ShadowRepo {
58
59
  async gitInit() {
59
60
  await core_1.fs.promises.mkdir(this.gitDir, { recursive: true });
60
61
  await git.init({ fs: core_1.fs, dir: this.projectPath, gitdir: this.gitDir, defaultBranch: 'main' });
61
- await this.locateIgnoreFiles();
62
62
  }
63
63
  /**
64
64
  * Delete the local tracking files
@@ -215,13 +215,10 @@ class ShadowRepo {
215
215
  dir: this.projectPath,
216
216
  gitdir: this.gitDir,
217
217
  trees: [git.WORKDIR()],
218
- // TODO: this can be marginally faster if we limit it to pkgDirs and toplevel project files
219
218
  // eslint-disable-next-line @typescript-eslint/require-await
220
219
  map: async (filepath) => filepath,
221
220
  }))
222
- .filter((filepath) => filepath.includes(gitIgnoreFileName) &&
223
- // can be top-level like '.' (no sep) OR must be in one of the package dirs
224
- (!filepath.includes(path.sep) || this.packageDirs.some((dir) => (0, functions_1.pathIsInFolder)(filepath, dir.name))))
221
+ .filter((filepath) => filepath.includes(gitIgnoreFileName))
225
222
  .map((ignoreFile) => path.join(this.projectPath, ignoreFile));
226
223
  }
227
224
  async stashIgnoreFile() {
@@ -1,2 +1,3 @@
1
1
  import { RemoteSyncInput } from './types';
2
2
  export declare const getMetadataKeyFromFileResponse: (fileResponse: RemoteSyncInput) => string[];
3
+ export declare const mappingsForSourceMemberTypesToMetadataType: Map<string, string>;
@@ -1,24 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getMetadataKeyFromFileResponse = void 0;
3
+ exports.mappingsForSourceMemberTypesToMetadataType = exports.getMetadataKeyFromFileResponse = void 0;
4
4
  /*
5
5
  * Copyright (c) 2020, salesforce.com, inc.
6
6
  * All rights reserved.
7
7
  * Licensed under the BSD 3-Clause license.
8
8
  * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
9
9
  */
10
- const path = require("path");
10
+ const path_1 = require("path");
11
+ const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
11
12
  const functions_1 = require("./functions");
12
- // LWC can have child folders (ex: dynamic templates like /templates/noDataIllustration.html
13
+ // See UT for examples of the complexity this must handle
14
+ // keys always use forward slashes, even on Windows
13
15
  const pathAfterFullName = (fileResponse) => fileResponse && fileResponse.filePath
14
- ? fileResponse.filePath.substr(fileResponse.filePath.indexOf(fileResponse.fullName)).replace(/\\/gi, '/')
16
+ ? (0, path_1.join)((0, path_1.dirname)(fileResponse.filePath).substring((0, path_1.dirname)(fileResponse.filePath).lastIndexOf(fileResponse.fullName)), (0, path_1.basename)(fileResponse.filePath)).replace(/\\/gi, '/')
15
17
  : '';
18
+ const registry = new source_deploy_retrieve_1.RegistryAccess();
19
+ // only compute once
20
+ const aliasTypes = registry
21
+ .getAliasTypes()
22
+ .map((aliasType) => [aliasType.name, registry.getTypeByName(aliasType.aliasFor).name]);
23
+ const reverseAliasTypes = new Map(aliasTypes.map(([alias, type]) => [type, alias]));
16
24
  // handle all "weird" type/name translation between SourceMember and SDR FileResponse
17
25
  // These get de-duplicated in a set later, so it's ok to have one per file
18
26
  const getMetadataKeyFromFileResponse = (fileResponse) => {
19
27
  // also create an element for the parent object
20
28
  if (fileResponse.type === 'CustomField' && fileResponse.filePath) {
21
- const splits = path.normalize(fileResponse.filePath).split(path.sep);
29
+ const splits = (0, path_1.normalize)(fileResponse.filePath).split(path_1.sep);
22
30
  const objectFolderIndex = splits.indexOf('objects');
23
31
  return [
24
32
  (0, functions_1.getMetadataKey)('CustomObject', splits[objectFolderIndex + 1]),
@@ -38,8 +46,27 @@ const getMetadataKeyFromFileResponse = (fileResponse) => {
38
46
  (0, functions_1.getMetadataKey)(fileResponse.type, fileResponse.fullName),
39
47
  ];
40
48
  }
41
- // standard key
49
+ // CustomLabels (file) => CustomLabel[] (how they're storedin SourceMembers)
50
+ if (fileResponse.type === 'CustomLabels' && fileResponse.filePath) {
51
+ return source_deploy_retrieve_1.ComponentSet.fromSource(fileResponse.filePath)
52
+ .getSourceComponents()
53
+ .toArray()
54
+ .flatMap((component) => component.getChildren().map((child) => (0, functions_1.getMetadataKey)('CustomLabel', child.fullName)));
55
+ }
56
+ // if we've aliased a type, we'll have to possibly sync both types--you can't tell from the sourceComponent retrieved which way it was stored on the server
57
+ if (reverseAliasTypes.has(fileResponse.type)) {
58
+ return [
59
+ (0, functions_1.getMetadataKey)(fileResponse.type, fileResponse.fullName),
60
+ (0, functions_1.getMetadataKey)(reverseAliasTypes.get(fileResponse.type), fileResponse.fullName),
61
+ ];
62
+ }
63
+ // standard key for everything else
42
64
  return [(0, functions_1.getMetadataKey)(fileResponse.type, fileResponse.fullName)];
43
65
  };
44
66
  exports.getMetadataKeyFromFileResponse = getMetadataKeyFromFileResponse;
67
+ exports.mappingsForSourceMemberTypesToMetadataType = new Map([
68
+ ...aliasTypes,
69
+ ['AuraDefinition', 'AuraDefinitionBundle'],
70
+ ['LightningComponentResource', 'LightningComponentBundle'],
71
+ ]);
45
72
  //# sourceMappingURL=metadataKeys.js.map
@@ -58,6 +58,12 @@ export declare class RemoteSourceTrackingService extends ConfigFile<RemoteSource
58
58
  static getInstance(options: RemoteSourceTrackingService.Options): Promise<RemoteSourceTrackingService>;
59
59
  static getFileName(): string;
60
60
  static getFilePath(orgId: string): string;
61
+ /**
62
+ * Delete the RemoteSourceTracking for a given org.
63
+ *
64
+ * @param orgId
65
+ * @returns the path of the deleted source tracking file
66
+ */
61
67
  static delete(orgId: string): Promise<string>;
62
68
  /**
63
69
  * Initializes the service with existing remote source tracking data, or sets
@@ -119,6 +125,7 @@ export declare class RemoteSourceTrackingService extends ConfigFile<RemoteSource
119
125
  * @param pollingTimeout maximum amount of time in seconds to poll for SourceMembers
120
126
  */
121
127
  pollForSourceTracking(expectedMembers: RemoteSyncInput[]): Promise<void>;
128
+ private calculateExpectedSourceMembers;
122
129
  private calculateTimeout;
123
130
  private querySourceMembersFrom;
124
131
  private querySourceMembersTo;
@@ -7,6 +7,7 @@
7
7
  */
8
8
  /* eslint-disable no-underscore-dangle */
9
9
  /* eslint-disable @typescript-eslint/member-ordering */
10
+ var _a;
10
11
  Object.defineProperty(exports, "__esModule", { value: true });
11
12
  exports.remoteChangeElementToChangeResult = exports.RemoteSourceTrackingService = void 0;
12
13
  const path = require("path");
@@ -16,6 +17,12 @@ const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
16
17
  const kit_1 = require("@salesforce/kit");
17
18
  const metadataKeys_1 = require("./metadataKeys");
18
19
  const functions_1 = require("./functions");
20
+ /*
21
+ * after some results have returned, how many times should we poll for missing sourcemembers
22
+ * even when there is a longer timeout remaining (because the deployment is very large)
23
+ */
24
+ const POLLING_DELAY_MS = 1000;
25
+ 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;
19
26
  /**
20
27
  * This service handles source tracking of metadata between a local project and an org.
21
28
  * Source tracking state is persisted to .sfdx/orgs/<orgId>/maxRevision.json.
@@ -82,6 +89,12 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
82
89
  static getFilePath(orgId) {
83
90
  return path.join('.sfdx', 'orgs', orgId, RemoteSourceTrackingService.getFileName());
84
91
  }
92
+ /**
93
+ * Delete the RemoteSourceTracking for a given org.
94
+ *
95
+ * @param orgId
96
+ * @returns the path of the deleted source tracking file
97
+ */
85
98
  static async delete(orgId) {
86
99
  const fileToDelete = RemoteSourceTrackingService.getFilePath(orgId);
87
100
  // the file might not exist, in which case we don't need to delete it
@@ -199,13 +212,9 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
199
212
  // Called during a source:tracking:reset
200
213
  this.setServerMaxRevision(0);
201
214
  this.initSourceMembers();
202
- let members;
203
- if (toRevision != null) {
204
- members = await this.querySourceMembersTo(toRevision);
205
- }
206
- else {
207
- members = await this.querySourceMembersFrom({ fromRevision: 0 });
208
- }
215
+ const members = toRevision != null
216
+ ? await this.querySourceMembersTo(toRevision)
217
+ : await this.querySourceMembersFrom({ fromRevision: 0 });
209
218
  await this.trackSourceMembers(members, true);
210
219
  return members.map((member) => (0, functions_1.getMetadataKey)(member.MemberType, member.MemberName));
211
220
  }
@@ -253,18 +262,20 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
253
262
  }
254
263
  let serverMaxRevisionCounter = this.getServerMaxRevision();
255
264
  sourceMembers.forEach((change) => {
265
+ var _a;
256
266
  // try accessing the sourceMembers object at the index of the change's name
257
267
  // if it exists, we'll update the fields - if it doesn't, we'll create and insert it
258
268
  const key = (0, functions_1.getMetadataKey)(change.MemberType, change.MemberName);
259
- let sourceMember = this.getSourceMember(key);
260
- if (sourceMember) {
269
+ const sourceMember = (_a = this.getSourceMember(key)) !== null && _a !== void 0 ? _a : {
270
+ serverRevisionCounter: change.RevisionCounter,
271
+ lastRetrievedFromServer: null,
272
+ memberType: change.MemberType,
273
+ isNameObsolete: change.IsNameObsolete,
274
+ };
275
+ if (sourceMember.lastRetrievedFromServer) {
261
276
  // We are already tracking this element so we'll update it
262
277
  if (!quiet) {
263
- let msg = `Updating ${key} to RevisionCounter: ${change.RevisionCounter}`;
264
- if (sync) {
265
- msg += ' and syncing';
266
- }
267
- this.logger.debug(msg);
278
+ this.logger.debug(`Updating ${key} to RevisionCounter: ${change.RevisionCounter}${sync ? ' and syncing' : ''}`);
268
279
  }
269
280
  sourceMember.serverRevisionCounter = change.RevisionCounter;
270
281
  sourceMember.isNameObsolete = change.IsNameObsolete;
@@ -272,18 +283,8 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
272
283
  else {
273
284
  // We are not yet tracking it so we'll insert a new record
274
285
  if (!quiet) {
275
- let msg = `Inserting ${key} with RevisionCounter: ${change.RevisionCounter}`;
276
- if (sync) {
277
- msg += ' and syncing';
278
- }
279
- this.logger.debug(msg);
286
+ this.logger.debug(`Inserting ${key} with RevisionCounter: ${change.RevisionCounter}${sync ? ' and syncing' : ''}`);
280
287
  }
281
- sourceMember = {
282
- serverRevisionCounter: change.RevisionCounter,
283
- lastRetrievedFromServer: null,
284
- memberType: change.MemberType,
285
- isNameObsolete: change.IsNameObsolete,
286
- };
287
288
  }
288
289
  // If we are syncing changes then we need to update the lastRetrievedFromServer field to
289
290
  // match the RevisionCounter from the SourceMember.
@@ -348,59 +349,59 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
348
349
  this.logger.warn('Not polling for SourceMembers since SFDX_DISABLE_SOURCE_MEMBER_POLLING = true.');
349
350
  return;
350
351
  }
351
- if (expectedMembers.length === 0) {
352
+ const outstandingSourceMembers = this.calculateExpectedSourceMembers(expectedMembers);
353
+ if (expectedMembers.length === 0 || outstandingSourceMembers.size === 0) {
352
354
  // Don't bother polling if we're not matching SourceMembers
353
355
  return;
354
356
  }
355
- const outstandingSourceMembers = new Map();
356
- // filter known Source tracking issues
357
- expectedMembers
358
- .filter((fileResponse) => {
359
- var _a;
360
- // unchanged files will never be in the sourceMembers. Not really sure why SDR returns them.
361
- return fileResponse.state !== source_deploy_retrieve_1.ComponentStatus.Unchanged &&
362
- // 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
363
- fileResponse.type !== 'CustomObject' &&
364
- // aura meta.xml aren't tracked as SourceMembers
365
- !((_a = fileResponse.filePath) === null || _a === void 0 ? void 0 : _a.endsWith('.cmp-meta.xml'));
366
- })
367
- .map((member) => {
368
- (0, metadataKeys_1.getMetadataKeyFromFileResponse)(member).map((key) => outstandingSourceMembers.set(key, member));
369
- });
370
- const originalSize = outstandingSourceMembers.size;
371
- const fromRevision = this.getServerMaxRevision();
357
+ const originalOutstandingSize = outstandingSourceMembers.size;
358
+ // this will be the absolute timeout from the start of the poll. We can also exit early if it doesn't look like more results are coming in
372
359
  const pollingTimeout = this.calculateTimeout(outstandingSourceMembers.size);
373
- this.logger.debug(`Polling for ${outstandingSourceMembers.size} SourceMembers from revision ${fromRevision} with timeout of ${pollingTimeout}s`);
360
+ let highestRevisionSoFar = this.getServerMaxRevision();
374
361
  let pollAttempts = 0;
362
+ let consecutiveEmptyResults = 0;
363
+ let someResultsReturned = false;
364
+ this.logger.debug(`Polling for ${outstandingSourceMembers.size} SourceMembers from revision ${highestRevisionSoFar} with timeout of ${pollingTimeout.seconds}s`);
375
365
  const poll = async () => {
376
366
  pollAttempts += 1; // not used to stop polling, but for debug logging
377
- // get sourceMembers since maxRevision
367
+ // get sourceMembers added since our most recent max
368
+ // use the "new highest" revision from the last poll that returned results
378
369
  const queriedMembers = await this.querySourceMembersFrom({
379
- fromRevision,
370
+ fromRevision: highestRevisionSoFar,
380
371
  quiet: pollAttempts !== 1,
381
372
  useCache: false,
382
373
  });
383
- // remove anything returned from the query list
384
- queriedMembers.map((member) => {
385
- outstandingSourceMembers.delete((0, functions_1.getMetadataKey)(member.MemberType, member.MemberName));
386
- });
387
- this.logger.debug(`[${pollAttempts}] Found ${originalSize - outstandingSourceMembers.size} of ${originalSize} SourceMembers`);
374
+ if (queriedMembers.length) {
375
+ queriedMembers.map((member) => {
376
+ // remove anything returned from the query list
377
+ outstandingSourceMembers.delete((0, functions_1.getMetadataKey)(member.MemberType, member.MemberName));
378
+ highestRevisionSoFar = Math.max(highestRevisionSoFar, member.RevisionCounter);
379
+ });
380
+ consecutiveEmptyResults = 0;
381
+ // flips on the first batch of results
382
+ someResultsReturned = true;
383
+ }
384
+ else {
385
+ consecutiveEmptyResults++;
386
+ }
387
+ this.logger.debug(`[${pollAttempts}] Found ${originalOutstandingSize - outstandingSourceMembers.size} of ${originalOutstandingSize} expected SourceMembers`);
388
388
  // update but don't sync
389
389
  await this.trackSourceMembers(queriedMembers, false);
390
390
  // exit if all have returned
391
391
  if (outstandingSourceMembers.size === 0) {
392
392
  return;
393
393
  }
394
- if (outstandingSourceMembers.size < 20) {
395
- this.logger.debug(outstandingSourceMembers.size < 20
396
- ? `Still looking for SourceMembers: ${Array.from(outstandingSourceMembers.keys()).join(',')}`
397
- : `Still looking for ${outstandingSourceMembers.size} Source Members`);
394
+ if (someResultsReturned && consecutiveEmptyResults >= CONSECUTIVE_EMPTY_POLLING_RESULT_LIMIT) {
395
+ throw new ts_retry_promise_1.NotRetryableError(`Polling found no results for ${consecutiveEmptyResults} consecutive attempts`);
398
396
  }
397
+ this.logger.debug(outstandingSourceMembers.size < 20
398
+ ? `Still looking for SourceMembers: ${Array.from(outstandingSourceMembers.keys()).join(',')}`
399
+ : `Still looking for ${outstandingSourceMembers.size} Source Members`);
399
400
  throw new Error();
400
401
  };
401
402
  const pollingFunction = (0, ts_retry_promise_1.retryDecorator)(poll, {
402
- timeout: pollingTimeout * 1000,
403
- delay: 1000,
403
+ timeout: pollingTimeout.milliseconds,
404
+ delay: POLLING_DELAY_MS,
404
405
  retries: 'INFINITELY',
405
406
  });
406
407
  try {
@@ -408,27 +409,71 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
408
409
  this.logger.debug(`Retrieved all SourceMember data after ${pollAttempts} attempts`);
409
410
  }
410
411
  catch {
411
- this.logger.warn(`Polling for SourceMembers timed out after ${pollAttempts} attempts`);
412
+ this.logger.warn(`Polling for SourceMembers timed out after ${pollAttempts} attempts (last ${consecutiveEmptyResults} were empty) )`);
412
413
  if (outstandingSourceMembers.size < 51) {
413
414
  this.logger.debug(`Could not find ${outstandingSourceMembers.size} SourceMembers: ${Array.from(outstandingSourceMembers).join(',')}`);
414
415
  }
415
416
  else {
416
417
  this.logger.debug(`Could not find SourceMembers for ${outstandingSourceMembers.size} components`);
417
418
  }
419
+ void core_1.Lifecycle.getInstance().emitTelemetry({
420
+ eventName: 'sourceMemberPollingTimeout',
421
+ library: 'SourceTracking',
422
+ timeoutSeconds: pollingTimeout.seconds,
423
+ attempts: pollAttempts,
424
+ consecutiveEmptyResults,
425
+ missingQuantity: outstandingSourceMembers.size,
426
+ deploymentSize: expectedMembers.length,
427
+ types: [...new Set(Array.from(outstandingSourceMembers.values()).map((member) => member.type))]
428
+ .sort()
429
+ .join(','),
430
+ members: Array.from(outstandingSourceMembers.keys()).join(','),
431
+ });
418
432
  }
419
433
  }
434
+ calculateExpectedSourceMembers(expectedMembers) {
435
+ const outstandingSourceMembers = new Map();
436
+ // filter known Source tracking issues
437
+ expectedMembers
438
+ .filter((fileResponse) => {
439
+ var _a, _b, _c, _d, _e;
440
+ // unchanged files will never be in the sourceMembers. Not really sure why SDR returns them.
441
+ return fileResponse.state !== source_deploy_retrieve_1.ComponentStatus.Unchanged &&
442
+ // 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
443
+ // we don't know which email folder type might be there, so don't require either
444
+ // Portal doesn't support source tracking, according to the coverage report
445
+ !['CustomObject', 'EmailFolder', 'EmailTemplateFolder', 'StandardValueSet', 'Portal'].includes(fileResponse.type) &&
446
+ // don't wait on workflow children
447
+ !fileResponse.type.startsWith('Workflow') &&
448
+ // aura xml aren't tracked as SourceMembers
449
+ !((_a = fileResponse.filePath) === null || _a === void 0 ? void 0 : _a.endsWith('.cmp-meta.xml')) &&
450
+ !((_b = fileResponse.filePath) === null || _b === void 0 ? void 0 : _b.endsWith('.tokens-meta.xml')) &&
451
+ !((_c = fileResponse.filePath) === null || _c === void 0 ? void 0 : _c.endsWith('.evt-meta.xml')) &&
452
+ !((_d = fileResponse.filePath) === null || _d === void 0 ? void 0 : _d.endsWith('.app-meta.xml')) &&
453
+ !((_e = fileResponse.filePath) === null || _e === void 0 ? void 0 : _e.endsWith('.intf-meta.xml'));
454
+ })
455
+ .map((member) => {
456
+ (0, metadataKeys_1.getMetadataKeyFromFileResponse)(member)
457
+ // CustomObject could have been added by the key generator
458
+ // remove some individual members known to not work with tracking even when their type does
459
+ .filter((key) => !key.startsWith('CustomObject') && key !== 'Profile__Standard' && key !== 'CustomTab__standard-home')
460
+ .map((key) => outstandingSourceMembers.set(key, member));
461
+ });
462
+ return outstandingSourceMembers;
463
+ }
420
464
  calculateTimeout(memberCount) {
421
- const overriddenTimeout = (0, kit_1.toNumber)(kit_1.env.getString('SFDX_SOURCE_MEMBER_POLLING_TIMEOUT', '0'));
465
+ var _a;
466
+ const overriddenTimeout = (_a = kit_1.env.getNumber('SFDX_SOURCE_MEMBER_POLLING_TIMEOUT', 0)) !== null && _a !== void 0 ? _a : 0;
422
467
  if (overriddenTimeout > 0) {
423
468
  this.logger.debug(`Overriding SourceMember polling timeout to ${overriddenTimeout}`);
424
- return overriddenTimeout;
469
+ return kit_1.Duration.seconds(overriddenTimeout);
425
470
  }
426
471
  // Calculate a polling timeout for SourceMembers based on the number of
427
472
  // member names being polled plus a buffer of 5 seconds. This will
428
473
  // wait 50s for each 1000 components, plus 5s.
429
474
  const pollingTimeout = Math.ceil(memberCount * 0.05) + 5;
430
475
  this.logger.debug(`Computed SourceMember polling timeout of ${pollingTimeout}s`);
431
- return pollingTimeout;
476
+ return kit_1.Duration.seconds(pollingTimeout);
432
477
  }
433
478
  async querySourceMembersFrom({ fromRevision, quiet = false, useCache = true, } = {}) {
434
479
  const rev = fromRevision != null ? fromRevision : this.getServerMaxRevision();
@@ -460,7 +505,7 @@ class RemoteSourceTrackingService extends core_1.ConfigFile {
460
505
  this.logger.debug(query);
461
506
  }
462
507
  try {
463
- const results = await this.org.getConnection().tooling.autoFetchQuery(query);
508
+ const results = await this.org.getConnection().tooling.autoFetchQuery(query, { maxFetch: 50000 });
464
509
  return results.records;
465
510
  }
466
511
  catch (error) {
@@ -477,18 +522,14 @@ RemoteSourceTrackingService.remoteSourceTrackingServiceDictionary = {};
477
522
  const remoteChangeElementToChangeResult = (rce) => {
478
523
  return {
479
524
  ...rce,
480
- ...(mappingsForSourceMemberTypesToMetadataType.has(rce.type)
525
+ ...(metadataKeys_1.mappingsForSourceMemberTypesToMetadataType.has(rce.type)
481
526
  ? {
482
527
  name: rce.name.split('/')[0],
483
- type: mappingsForSourceMemberTypesToMetadataType.get(rce.type),
528
+ type: metadataKeys_1.mappingsForSourceMemberTypesToMetadataType.get(rce.type),
484
529
  }
485
530
  : {}),
486
531
  origin: 'remote', // we know they're remote
487
532
  };
488
533
  };
489
534
  exports.remoteChangeElementToChangeResult = remoteChangeElementToChangeResult;
490
- const mappingsForSourceMemberTypesToMetadataType = new Map([
491
- ['AuraDefinition', 'AuraDefinitionBundle'],
492
- ['LightningComponentResource', 'LightningComponentBundle'],
493
- ]);
494
535
  //# sourceMappingURL=remoteSourceTrackingService.js.map
@@ -18,6 +18,7 @@ const remoteSourceTrackingService_1 = require("./shared/remoteSourceTrackingServ
18
18
  const localShadowRepo_1 = require("./shared/localShadowRepo");
19
19
  const guards_1 = require("./shared/guards");
20
20
  const functions_1 = require("./shared/functions");
21
+ const metadataKeys_1 = require("./shared/metadataKeys");
21
22
  /**
22
23
  * Manages source tracking files (remote and local)
23
24
  *
@@ -519,6 +520,9 @@ class SourceTracking extends kit_1.AsyncCreatable {
519
520
  }
520
521
  registrySupportsType(type) {
521
522
  try {
523
+ if (metadataKeys_1.mappingsForSourceMemberTypesToMetadataType.has(type)) {
524
+ return true;
525
+ }
522
526
  // this must use getTypeByName because findType doesn't support addressable child types (ex: customField!)
523
527
  this.registry.getTypeByName(type);
524
528
  return true;
@@ -551,11 +555,16 @@ class SourceTracking extends kit_1.AsyncCreatable {
551
555
  const remoteChangesAsComponentSet = new source_deploy_retrieve_1.ComponentSet(remoteChangesAsMetadataMember);
552
556
  this.logger.debug(` the generated component set has ${remoteChangesAsComponentSet.size.toString()} items`);
553
557
  if (remoteChangesAsComponentSet.size < elements.length) {
558
+ // there *could* be something missing
559
+ // some types (ex: LWC) show up as multiple files in the remote changes, but only one in the component set
554
560
  // iterate the elements to see which ones didn't make it into the component set
555
- throw new Error(`unable to generate complete component set for ${elements
556
- .filter((element) => !remoteChangesAsComponentSet.has({ type: element === null || element === void 0 ? void 0 : element.type, fullName: element === null || element === void 0 ? void 0 : element.name }))
557
- .map((element) => `${element.name} (${element.type})`)
558
- .join(os_1.EOL)}`);
561
+ const missingComponents = elements.filter((element) => !remoteChangesAsComponentSet.has({ type: element === null || element === void 0 ? void 0 : element.type, fullName: element === null || element === void 0 ? void 0 : element.name }));
562
+ // Throw if anything was actually missing
563
+ if (missingComponents.length > 0) {
564
+ throw new Error(`unable to generate complete component set for ${elements
565
+ .map((element) => `${element.name} (${element.type})`)
566
+ .join(os_1.EOL)}`);
567
+ }
559
568
  }
560
569
  const matchingLocalSourceComponentsSet = source_deploy_retrieve_1.ComponentSet.fromSource({
561
570
  fsPaths: this.packagesDirs.map((dir) => dir.path),
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "sourceTrackingFileVersionMismatch": "This project uses the %s version of source tracking files.",
3
3
  "clearSuggestion": "Clear the %s version of the tracking files by running '%s'",
4
- "useOtherVersion": "Use the %s version of the command, '%s' to preserve the tracking files)"
4
+ "useOtherVersion": "Use the %s version of the command, '%s' to preserve the tracking files"
5
5
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@salesforce/source-tracking",
3
3
  "description": "API for tracking local and remote Salesforce metadata changes",
4
- "version": "0.5.2",
4
+ "version": "1.1.0",
5
5
  "author": "Salesforce",
6
6
  "license": "BSD-3-Clause",
7
7
  "main": "lib/index.js",
@@ -25,7 +25,7 @@
25
25
  "prune:dead": "ts-prune | grep -v 'source-deploy-retrieve' | grep -v 'index.ts'",
26
26
  "test": "sf-test",
27
27
  "test:nuts": "nyc mocha \"**/*.nut.ts\" --slow 4500 --timeout 600000 --parallel",
28
- "test:nuts:local": "mocha \"**/local*.nut.ts\" --slow 4500 --timeout 600000 --parallel"
28
+ "test:nuts:local": "mocha \"**/local/*.nut.ts\" --slow 4500 --timeout 600000 --parallel"
29
29
  },
30
30
  "keywords": [
31
31
  "force",
@@ -45,7 +45,7 @@
45
45
  "dependencies": {
46
46
  "@salesforce/core": "^2.33.1",
47
47
  "@salesforce/kit": "^1.5.17",
48
- "@salesforce/source-deploy-retrieve": "^5.8.2",
48
+ "@salesforce/source-deploy-retrieve": "^5.9.4",
49
49
  "isomorphic-git": "^1.9.2",
50
50
  "ts-retry-promise": "^0.6.0"
51
51
  },
@@ -66,7 +66,7 @@
66
66
  "eslint-config-salesforce-license": "^0.1.6",
67
67
  "eslint-config-salesforce-typescript": "^0.2.7",
68
68
  "eslint-plugin-header": "^3.1.1",
69
- "eslint-plugin-import": "2.24.2",
69
+ "eslint-plugin-import": "2.25.4",
70
70
  "eslint-plugin-jsdoc": "^37.0.1",
71
71
  "eslint-plugin-prettier": "^3.4.0",
72
72
  "husky": "^7.0.4",