@salesforce/source-tracking
Version:
API for tracking local and remote Salesforce metadata changes
649 lines • 31.2 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SourceTracking = void 0;
/*
* Copyright 2025, Salesforce, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
const fs = __importStar(require("node:fs"));
const node_path_1 = require("node:path");
const core_1 = require("@salesforce/core");
const kit_1 = require("@salesforce/kit");
const ts_types_1 = require("@salesforce/ts-types");
const source_deploy_retrieve_1 = require("@salesforce/source-deploy-retrieve");
// this is not exported by SDR (see the comments in SDR regarding its limitations)
const filePathGenerator_1 = require("@salesforce/source-deploy-retrieve/lib/src/utils/filePathGenerator");
const remoteSourceTrackingService_1 = require("./shared/remote/remoteSourceTrackingService");
const localShadowRepo_1 = require("./shared/local/localShadowRepo");
const conflicts_1 = require("./shared/conflicts");
const guards_1 = require("./shared/guards");
const remoteChangeIgnoring_1 = require("./shared/remoteChangeIgnoring");
const functions_1 = require("./shared/functions");
const functions_2 = require("./shared/functions");
const metadataKeys_1 = require("./shared/metadataKeys");
const populateFilePaths_1 = require("./shared/populateFilePaths");
const populateTypesAndNames_1 = require("./shared/populateTypesAndNames");
const localComponentSetArray_1 = require("./shared/localComponentSetArray");
const functions_3 = require("./shared/functions");
/**
* Manages source tracking files (remote and local)
*
* const tracking = await SourceTracking.create({org: this.org, project: this.project});
*
*/
class SourceTracking extends kit_1.AsyncCreatable {
registry;
projectPath;
org;
project;
packagesDirs;
logger;
// remote and local tracking may not exist if not initialized
localRepo;
remoteSourceTrackingService;
forceIgnore;
ignoreConflicts;
subscribeSDREvents;
ignoreLocalCache;
orgId;
constructor(options) {
super(options);
this.org = options.org;
this.orgId = this.org.getOrgId();
this.projectPath = options.project.getPath();
this.packagesDirs = options.project.getPackageDirectories();
this.logger = core_1.Logger.childFromRoot('SourceTracking');
this.project = options.project;
this.ignoreConflicts = options.ignoreConflicts ?? false;
this.ignoreLocalCache = options.ignoreLocalCache ?? false;
this.subscribeSDREvents = options.subscribeSDREvents ?? false;
this.registry = options.registry ?? new source_deploy_retrieve_1.RegistryAccess(undefined, this.projectPath);
}
async init() {
await this.maybeSubscribeLifecycleEvents();
}
/**
*
* @param byPackageDir if true, returns a ComponentSet for each packageDir that has any changes
* * if false, returns an array containing one ComponentSet with all changes
* * if not specified, this method will follow what sfdx-project.json says
* @returns ComponentSet[]
*/
async localChangesAsComponentSet(byPackageDir) {
const [projectConfig] = await Promise.all([
this.project.resolveProjectConfig(),
this.ensureLocalTracking(),
]);
const sourceApiVersion = projectConfig.sourceApiVersion;
const [nonDeletes, deletes] = await Promise.all([
this.localRepo.getNonDeleteFilenames(),
this.localRepo.getDeleteFilenames(),
]);
// it'll be easier to filter filenames and work with smaller component sets than to filter SourceComponents
const groupings = (0, localComponentSetArray_1.getGroupedFiles)({
packageDirs: this.packagesDirs,
nonDeletes,
deletes,
}, byPackageDir ?? Boolean(projectConfig.pushPackageDirectoriesSequentially)); // if the users specified true or false for the param, that overrides the project config
this.logger.debug(`will build array of ${groupings.length} componentSet(s)`);
return (0, localComponentSetArray_1.getComponentSets)({ groupings, sourceApiVersion, registry: this.registry });
}
/** reads tracking files for remote changes. It DOES NOT consider the effects of .forceignore unless told to */
async remoteNonDeletesAsComponentSet({ applyIgnore = false, } = {}) {
if (applyIgnore) {
this.forceIgnore ??= source_deploy_retrieve_1.ForceIgnore.findAndCreate(this.project.getDefaultPackage().path);
}
const [changeResults, sourceBackedComponents, projectConfig] = await Promise.all([
// all changes based on remote tracking
this.getChanges({
origin: 'remote',
state: 'nondelete',
format: 'ChangeResult',
}),
// only returns source-backed components (SBC)
this.getChanges({
origin: 'remote',
state: 'nondelete',
format: 'SourceComponent',
}),
this.project.resolveProjectConfig(),
]);
const componentSet = new source_deploy_retrieve_1.ComponentSet(applyIgnore ? sourceBackedComponents.filter(noFileIsIgnored(this.forceIgnore)) : sourceBackedComponents, this.registry);
// there may be remote adds not in the SBC. So we add those manually
(applyIgnore
? (0, remoteChangeIgnoring_1.removeIgnored)(changeResults, this.forceIgnore, this.project.getDefaultPackage().fullPath, this.registry)
: changeResults.map(functions_1.remoteChangeToMetadataMember)).map((mm) => {
componentSet.add(mm);
});
if (projectConfig.sourceApiVersion) {
componentSet.sourceApiVersion = projectConfig.sourceApiVersion;
}
return componentSet;
}
/**
* Does most of the work for the force:source:status command.
* Outputs need a bit of massage since this aims to provide nice json.
*
* @param local you want local status
* @param remote you want remote status
* @returns StatusOutputRow[]
*/
async getStatus({ local, remote }) {
let results = [];
if (local) {
results = results.concat(await this.getLocalStatusRows());
}
if (remote) {
await this.ensureRemoteTracking(true);
const [remoteDeletes, remoteModifies] = await Promise.all([
this.getChanges({ origin: 'remote', state: 'delete', format: 'ChangeResult' }),
this.getChanges({ origin: 'remote', state: 'nondelete', format: 'ChangeResultWithPaths' }),
]);
results = results.concat((await Promise.all(remoteDeletes.concat(remoteModifies).map((item) => this.remoteChangesToOutputRows(item)))).flat(1));
}
if (local && remote) {
// keys like ApexClass__MyClass.cls
const conflictFiles = new Set((await this.getConflicts()).flatMap((conflict) => conflict.filenames).filter(ts_types_1.isString));
results = results.map((row) => ({
...row,
conflict: !!row.filePath && conflictFiles.has(row.filePath),
}));
}
return results;
}
async getChanges(options) {
if (options?.origin === 'local') {
await this.ensureLocalTracking();
const filenames = await getLocalChangesAsFilenames(this.localRepo)(options.state);
if (options.format === 'string') {
return filenames;
}
if (options.format === 'ChangeResult' || options.format === 'ChangeResultWithPaths') {
return filenames.map((filename) => ({
filenames: [filename],
origin: 'local',
}));
}
if (options.format === 'SourceComponent') {
const resolver = new source_deploy_retrieve_1.MetadataResolver(this.registry, options.state === 'delete' ? source_deploy_retrieve_1.VirtualTreeContainer.fromFilePaths(filenames) : undefined);
return filenames
.flatMap((filename) => {
try {
return resolver.getComponentsFromPath(filename);
}
catch (e) {
this.logger.warn(`unable to resolve ${filename}`);
return undefined;
}
})
.filter(guards_1.isDefined);
}
}
if (options?.origin === 'remote') {
await this.ensureRemoteTracking();
const remoteChanges = await this.remoteSourceTrackingService.retrieveUpdates();
this.logger.debug('remoteChanges', remoteChanges);
const filteredChanges = remoteChanges
.filter(remoteFilterByState[options.state])
// skip any remote types not in the registry. Will emit warnings
.filter((rce) => (0, metadataKeys_1.registrySupportsType)(this.registry)(rce.type));
if (options.format === 'ChangeResult') {
return filteredChanges.map((0, remoteSourceTrackingService_1.remoteChangeElementToChangeResult)(this.registry));
}
if (options.format === 'ChangeResultWithPaths') {
return (0, populateFilePaths_1.populateFilePaths)({
elements: filteredChanges.map((0, remoteSourceTrackingService_1.remoteChangeElementToChangeResult)(this.registry)),
packageDirPaths: this.project.getPackageDirectories().map((pkgDir) => pkgDir.fullPath),
registry: this.registry,
});
}
// turn it into a componentSet to resolve filenames
const remoteChangesAsComponentSet = new source_deploy_retrieve_1.ComponentSet(filteredChanges.map((element) => ({
type: element?.type,
fullName: element?.name,
})), this.registry);
const matchingLocalSourceComponentsSet = source_deploy_retrieve_1.ComponentSet.fromSource({
fsPaths: this.packagesDirs.map((dir) => (0, node_path_1.resolve)(dir.fullPath)),
include: remoteChangesAsComponentSet,
registry: this.registry,
});
if (options.format === 'string') {
return matchingLocalSourceComponentsSet.getSourceComponents().toArray().flatMap(functions_2.getAllFiles);
}
else if (options.format === 'SourceComponent') {
return matchingLocalSourceComponentsSet.getSourceComponents().toArray();
}
}
throw new Error(`unsupported options: ${JSON.stringify(options)}`);
}
async maybeApplyRemoteDeletesToLocal(returnDeleteFileResponses) {
const changesToDelete = await this.getChanges({ origin: 'remote', state: 'delete', format: 'SourceComponent' });
const fileResponsesFromDelete = await this.deleteFilesAndUpdateTracking(changesToDelete);
return returnDeleteFileResponses
? {
componentSetFromNonDeletes: await this.remoteNonDeletesAsComponentSet({ applyIgnore: true }),
fileResponsesFromDelete,
}
: this.remoteNonDeletesAsComponentSet({ applyIgnore: true });
}
/**
*
* returns immediately if there are no changesToDelete
*
* @param changesToDelete array of SourceComponent
*/
async deleteFilesAndUpdateTracking(changesToDelete) {
if (changesToDelete.length === 0) {
return [];
}
const sourceComponentByFileName = new Map(changesToDelete.flatMap((component) => (0, functions_2.getAllFiles)(component).map((filename) => [filename, component])));
// calculate what to return before we delete any files and .walkContent is no longer valid
const changedToBeDeleted = changesToDelete.flatMap((component) => (0, functions_2.getAllFiles)(component).map((file) => ({
state: source_deploy_retrieve_1.ComponentStatus.Deleted,
filePath: file,
type: component.type.name,
fullName: component.fullName,
})));
// original CustomLabels behavior
const nonDecomposedLabels = this.registry.getTypeByName('customlabels').strategies?.transformer === 'nonDecomposed';
const filenames = Array.from(sourceComponentByFileName.keys());
// delete the files
await Promise.all(filenames.map((filename) => sourceComponentByFileName.get(filename)?.type.id === 'customlabel' && nonDecomposedLabels
? (0, functions_2.deleteCustomLabels)(filename, changesToDelete.filter(functions_3.sourceComponentIsCustomLabel))
: fs.promises.unlink(filename)));
// update the tracking files. We're simulating SDR-style fileResponse
await Promise.all([
this.updateLocalTracking({ deletedFiles: filenames }),
this.updateRemoteTracking(changesToDelete.map((component) => ({
type: component.type.name,
fullName: component.fullName,
state: source_deploy_retrieve_1.ComponentStatus.Deleted,
})), true // skip polling because it's a pull
),
]);
return changedToBeDeleted;
}
/**
* Update tracking for the options passed.
*
* @param options the files to update
*/
async updateLocalTracking(options) {
this.logger.trace('start: updateLocalTracking', options);
await this.ensureLocalTracking();
this.logger.trace('files', options.files);
// relative paths make smaller trees AND isogit wants them relative
const relativeOptions = {
files: (options.files ?? []).map((0, functions_2.ensureRelative)(this.projectPath)),
deletedFiles: (options.deletedFiles ?? []).map((0, functions_2.ensureRelative)(this.projectPath)),
};
// plot twist: if you delete a member of a bundle (ex: lwc/foo/foo.css) and push, it'll not be in the fileResponses (deployedFiles) or deletedFiles
// what got deleted? Any local changes NOT in the fileResponses but part of a successfully deployed bundle
const deployedFilesAsVirtualComponentSet = source_deploy_retrieve_1.ComponentSet.fromSource({
// resolve from highest possible level. TODO: can we use [.]
fsPaths: relativeOptions.files.length ? [relativeOptions.files[0].split(node_path_1.sep)[0]] : [],
tree: source_deploy_retrieve_1.VirtualTreeContainer.fromFilePaths(relativeOptions.files),
registry: this.registry,
});
// these are top-level bundle paths like lwc/foo
const bundlesWithDeletedFiles = (await this.getChanges({ origin: 'local', state: 'delete', format: 'SourceComponent' }))
.filter(functions_2.supportsPartialDelete)
.filter((cmp) => deployedFilesAsVirtualComponentSet.has({ type: cmp.type, fullName: cmp.fullName }))
.map((cmp) => cmp.content)
.filter(ts_types_1.isString);
await this.localRepo.commitChanges({
deployedFiles: relativeOptions.files,
deletedFiles: relativeOptions.deletedFiles.concat((await this.localRepo.getDeleteFilenames()).filter((deployedFile) => bundlesWithDeletedFiles.some((0, functions_2.folderContainsPath)(deployedFile)) &&
!relativeOptions.files.includes(deployedFile))),
});
this.logger.trace('done: updateLocalTracking', options);
}
/**
* Mark remote source tracking files so say that we have received the latest version from the server
* Optional skip polling for the SourceMembers to exist on the server and be updated in local files
*/
async updateRemoteTracking(fileResponses, skipPolling = false) {
// false to explicitly NOT query until we do the polling
await this.ensureRemoteTracking(false);
if (!skipPolling) {
// poll to make sure we have the updates before syncing the ones from metadataKeys
await this.remoteSourceTrackingService.pollForSourceTracking(this.registry, fileResponses);
}
await this.remoteSourceTrackingService.syncSpecifiedElements(this.registry, fileResponses);
}
async reReadLocalTrackingCache() {
await this.localRepo.getStatus(true);
}
/**
* If the local tracking shadowRepo doesn't exist, it will be created.
* 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
* Useful before parallel operations
*/
async ensureLocalTracking() {
if (this.localRepo) {
if (this.ignoreLocalCache) {
await this.localRepo.getStatus(true);
}
return;
}
this.localRepo = await localShadowRepo_1.ShadowRepo.getInstance({
orgId: this.orgId,
projectPath: (0, node_path_1.normalize)(this.projectPath),
packageDirs: this.packagesDirs,
registry: this.registry,
});
// loads the status from file so that it's cached
await this.localRepo.getStatus();
}
/**
* If the remote tracking shadowRepo doesn't exist, it will be created.
* Does nothing if it already exists.
* Useful before parallel operations
*/
async ensureRemoteTracking(initializeWithQuery = false) {
if (this.remoteSourceTrackingService) {
this.logger.debug('ensureRemoteTracking: remote tracking already exists');
return;
}
this.logger.debug('ensureRemoteTracking: remote tracking does not exist yet; getting instance');
this.remoteSourceTrackingService = await remoteSourceTrackingService_1.RemoteSourceTrackingService.getInstance({
org: this.org,
projectPath: this.projectPath,
});
if (initializeWithQuery) {
await this.remoteSourceTrackingService.retrieveUpdates();
}
}
/**
* Deletes the local tracking shadowRepo
* return the list of files that were in it
*/
async clearLocalTracking() {
await this.ensureLocalTracking();
return this.localRepo.delete();
}
/**
* Commits all the local changes so that no changes are present in status
*/
async resetLocalTracking() {
await this.ensureLocalTracking();
const [deletes, nonDeletes] = await Promise.all([
this.localRepo.getDeleteFilenames(),
this.localRepo.getNonDeleteFilenames(),
]);
await this.localRepo.commitChanges({
deletedFiles: deletes,
deployedFiles: nonDeletes,
message: 'via resetLocalTracking',
});
return [...deletes, ...nonDeletes];
}
/**
* Deletes the remote tracking files
*/
async clearRemoteTracking() {
return remoteSourceTrackingService_1.RemoteSourceTrackingService.delete(this.orgId);
}
/**
* Sets the files to max revision so that no changes appear
*/
async resetRemoteTracking(serverRevision) {
await this.ensureRemoteTracking();
const resetMembers = await this.remoteSourceTrackingService.reset(serverRevision);
return resetMembers.length;
}
/**
* Compares local and remote changes to detect conflicts
*/
async getConflicts() {
// we're going to need have both initialized
await Promise.all([this.ensureRemoteTracking(), this.ensureLocalTracking()]);
// Strategy: check local changes first (since it'll be faster) to avoid callout
// early return if either local or remote is empty
const localChanges = await this.getChanges({
state: 'nondelete',
origin: 'local',
format: 'ChangeResult',
});
if (localChanges.length === 0) {
return [];
}
const remoteChanges = await this.getChanges({
origin: 'remote',
state: 'nondelete',
// remote adds won't have a filename, so we ask for it to be resolved
format: 'ChangeResultWithPaths',
});
if (remoteChanges.length === 0) {
return [];
}
this.forceIgnore ??= source_deploy_retrieve_1.ForceIgnore.findAndCreate(this.project.getDefaultPackage().path);
const result = (0, conflicts_1.getDedupedConflictsFromChanges)({
localChanges,
remoteChanges,
projectPath: this.projectPath,
forceIgnore: this.forceIgnore,
registry: this.registry,
});
return result;
}
/**
* handles both remote and local tracking
*
* @param result FileResponse[]
*/
async updateTrackingFromDeploy(deployResult) {
const successes = deployResult.getFileResponses().filter(guards_1.isSdrSuccess).filter(guards_1.FileResponseHasPath);
if (!successes.length) {
return;
}
await Promise.all([
this.updateLocalTracking({
// assertions allowed because filtered above
files: successes.filter(guards_1.FileResponseIsNotDeleted).map(filePathFromFileResponse),
deletedFiles: successes.filter(guards_1.FileResponseIsDeleted).map(filePathFromFileResponse),
}),
this.updateRemoteTracking(successes.map(functions_1.FileResponseSuccessToRemoteSyncInput)),
]);
}
/**
* handles both remote and local tracking
*
* @param result FileResponse[]
*/
async updateTrackingFromRetrieve(retrieveResult) {
const successes = retrieveResult.getFileResponses().filter(guards_1.isSdrSuccess);
if (!successes.length) {
return;
}
await Promise.all([
this.updateLocalTracking({
// assertion allowed because it's filtering out undefined
files: successes.filter(guards_1.FileResponseIsNotDeleted).filter(guards_1.FileResponseHasPath).map(filePathFromFileResponse),
deletedFiles: successes.filter(guards_1.FileResponseIsDeleted).filter(guards_1.FileResponseHasPath).map(filePathFromFileResponse),
}),
this.updateRemoteTracking(successes.map(functions_1.FileResponseSuccessToRemoteSyncInput), true // retrieves don't need to poll for SourceMembers
),
]);
}
/**
* If you've already got an instance of STL, but need to change the conflicts setting
* normally you set this on instantiation
*
* @param value true/false
*/
setIgnoreConflicts(value) {
this.ignoreConflicts = value;
}
async maybeSubscribeLifecycleEvents() {
if (this.subscribeSDREvents && (await this.org.tracksSource())) {
const lifecycle = core_1.Lifecycle.getInstance();
// the only thing STL uses pre events for is to check conflicts. So if you don't care about conflicts, don't listen!
if (!this.ignoreConflicts) {
this.logger.debug('subscribing to predeploy/retrieve events');
// subscribe to SDR `pre` events to handle conflicts before deploy/retrieve
lifecycle.on('scopedPreDeploy', async (e) => {
this.logger.debug('received scopedPreDeploy event');
if (e.orgId === this.orgId) {
(0, conflicts_1.throwIfConflicts)((0, conflicts_1.findConflictsInComponentSet)(e.componentSet, await this.getConflicts()));
}
}, `stl#scopedPreDeploy-${this.orgId}`);
lifecycle.on('scopedPreRetrieve', async (e) => {
this.logger.debug('received scopedPreRetrieve event');
if (e.orgId === this.orgId) {
(0, conflicts_1.throwIfConflicts)((0, conflicts_1.findConflictsInComponentSet)(e.componentSet, await this.getConflicts()));
}
}, `stl#scopedPreRetrieve-${this.orgId}`);
}
// subscribe to SDR post-deploy event
this.logger.debug('subscribing to postdeploy/retrieve events');
// yes, the post hooks really have different payloads!
lifecycle.on('scopedPostDeploy', async (e) => {
this.logger.debug('received scopedPostDeploy event');
if (e.orgId === this.orgId && e.deployResult.response.success) {
await this.updateTrackingFromDeploy(e.deployResult);
}
}, `stl#scopedPostDeploy-${this.orgId}`);
lifecycle.on('scopedPostRetrieve', async (e) => {
this.logger.debug('received scopedPostRetrieve event');
if (e.orgId === this.orgId && e.retrieveResult.response.success) {
await this.updateTrackingFromRetrieve(e.retrieveResult);
}
}, `stl#scopedPostRetrieve-${this.orgId}`);
}
}
async getLocalStatusRows() {
await this.ensureLocalTracking();
this.forceIgnore ??= source_deploy_retrieve_1.ForceIgnore.findAndCreate(this.project.getDefaultPackage().path); // ensure forceignore is initialized
const [adds, modifies, deletes] = await Promise.all(['add', 'modify', 'delete'].map((state) => this.getChanges({ origin: 'local', state, format: 'ChangeResult' })));
const base = { projectPath: this.projectPath, registry: this.registry, excludeUnresolvable: true };
const toOutput = localChangesToOutputRow(this.logger)(this.forceIgnore);
return [
...(0, populateTypesAndNames_1.populateTypesAndNames)(base)(adds).flatMap(toOutput('add')),
...(0, populateTypesAndNames_1.populateTypesAndNames)(base)(modifies).flatMap(toOutput('modify')),
...(0, populateTypesAndNames_1.populateTypesAndNames)({ ...base, resolveDeleted: true })(deletes).flatMap(toOutput('delete')),
];
}
// reserve the right to do something more sophisticated in the future
// via async for figuring out hypothetical filenames (ex: getting default packageDir)
// eslint-disable-next-line @typescript-eslint/require-await
async remoteChangesToOutputRows(input) {
this.logger.debug('converting ChangeResult to a row', input);
this.forceIgnore ??= source_deploy_retrieve_1.ForceIgnore.findAndCreate(this.project.getDefaultPackage().path);
const baseObject = {
type: input.type ?? '',
origin: input.origin,
state: stateFromChangeResult(input),
fullName: input.name ?? '',
};
// it's easy to check ignores if the filePaths exist locally
if (input.filenames?.length) {
return input.filenames.map((filename) => ({
...baseObject,
filePath: filename,
ignored: this.forceIgnore.denies(filename),
}));
}
// when the file doesn't exist locally, there are no filePaths
// SDR can generate the hypothetical place it *would* go and check that
if ((0, guards_1.isChangeResultWithNameAndType)(input)) {
const ignored = (0, filePathGenerator_1.filePathsFromMetadataComponent)((0, functions_1.changeResultToMetadataComponent)(this.registry)(input)).some((0, functions_2.forceIgnoreDenies)(this.forceIgnore));
return [
{
...baseObject,
ignored,
},
];
}
return [baseObject];
}
}
exports.SourceTracking = SourceTracking;
const remoteFilterByState = {
add: (change) => !change.deleted && !change.modified,
modify: (change) => change.modified === true,
delete: (change) => change.deleted === true,
nondelete: (change) => !change.deleted,
};
const stateFromChangeResult = (input) => {
if (input.deleted) {
return 'delete';
}
if (input.modified) {
return 'modify';
}
return 'add';
};
const getLocalChangesAsFilenames = (localRepo) => async (state) => {
switch (state) {
case 'modify':
return localRepo.getModifyFilenames();
case 'nondelete':
return localRepo.getNonDeleteFilenames();
case 'delete':
return localRepo.getDeleteFilenames();
case 'add':
return localRepo.getAddFilenames();
}
};
const filePathFromFileResponse = (input) => input.filePath;
const noFileIsIgnored = (forceIgnore) => (cmp) => !(0, functions_2.getAllFiles)(cmp).some((0, functions_2.forceIgnoreDenies)(forceIgnore));
const localChangesToOutputRow = (logger) => (forceIgnore) => (localType) => (input) => {
logger.debug('converting ChangeResult to a row', input);
if (input.filenames) {
return input.filenames.map((filename) => ({
type: input.type ?? '',
state: localType,
fullName: input.name ?? '',
filePath: filename,
origin: 'local',
ignored: forceIgnore.denies(filename),
}));
}
throw new Error('no filenames found for local ChangeResult');
};
//# sourceMappingURL=sourceTracking.js.map