UNPKG

@salesforce/core

Version:

Core libraries to interact with SFDX projects, orgs, and APIs.

115 lines 5.3 kB
"use strict"; /* * Copyright (c) 2020, salesforce.com, inc. * All rights reserved. * Licensed under the BSD 3-Clause license. * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Lifecycle = void 0; const Debug = require("debug"); /** * An asynchronous event listener and emitter that follows the singleton pattern. The singleton pattern allows lifecycle * events to be emitted from deep within a library and still be consumed by any other library or tool. It allows other * developers to react to certain situations or events in your library without them having to manually call the method themselves. * * An example might be transforming metadata before it is deployed to an environment. As long as an event was emitted from the * deploy library and you were listening on that event in the same process, you could transform the metadata before the deploy * regardless of where in the code that metadata was initiated. * * @example * ``` * // Listen for an event in a plugin hook * Lifecycle.getInstance().on('deploy-metadata', transformMetadata) * * // Deep in the deploy code, fire the event for all libraries and plugins to hear. * Lifecycle.getInstance().emit('deploy-metadata', metadataToBeDeployed); * ``` */ class Lifecycle { constructor() { this.debug = Debug(`sfdx:${this.constructor.name}`); this.listeners = {}; } /** * Retrieve the singleton instance of this class so that all listeners and emitters can interact from any library or tool */ static getInstance() { // Across a npm dependency tree, there may be a LOT of versions of `@salesforce/core`. We want to ensure that consumers are notified when // listening on a lifecycle event that is fired by a different version of `@salesforce/core`. Adding the instance on the global object will // ensure this. // // For example, a consumer calls `Lifecycle.getInstance().on('myEvent', ...)` on version `@salesforce/core@2.12.2`, and another consumer calls // `Lifecycle.getInstance().emit('myEvent', ...)` on version `@salesforce/core@2.13.0`, the on handler will never be called. // // Note: If ANYTHING is ever added to this class, it needs to check and update `global.salesforceCoreLifecycle` to the newer version. // One way this can be done by adding a `version = require(../package.json).version` to the Lifecycle class, then checking if // `global.salesforceCoreLifecycle` is greater or equal to that version. // // For example, let's say a new method is added in `@salesforce/core@3.0.0`. If `Lifecycle.getInstance()` is called fist by // `@salesforce/core@2.12.2` then by someone who depends on version `@salesforce/core@3.0.0` (who depends on the new method) // they will get a "method does not exist on object" error because the instance on the global object will be of `@salesforce/core@2.12.2`. // // Nothing should EVER be removed, even across major versions. if (!global.salesforceCoreLifecycle) { global.salesforceCoreLifecycle = new Lifecycle(); } return global.salesforceCoreLifecycle; } /** * Remove all listeners for a given event * * @param eventName The name of the event to remove listeners of */ removeAllListeners(eventName) { this.listeners[eventName] = []; } /** * Get an array of listeners (callback functions) for a given event * * @param eventName The name of the event to get listeners of */ getListeners(eventName) { const listeners = this.listeners[eventName]; if (listeners) { return listeners; } else { this.listeners[eventName] = []; return []; } } /** * Create a new listener for a given event * * @param eventName The name of the event that is being listened for * @param cb The callback function to run when the event is emitted */ on(eventName, cb) { const listeners = this.getListeners(eventName); if (listeners.length !== 0) { this.debug(`${listeners.length + 1} lifecycle events with the name ${eventName} have now been registered. When this event is emitted all ${listeners.length + 1} listeners will fire.`); } listeners.push(cb); this.listeners[eventName] = listeners; } /** * Emit a given event, causing all callback functions to be run in the order they were registered * * @param eventName The name of the event to emit * @param data The argument to be passed to the callback function */ async emit(eventName, data) { const listeners = this.getListeners(eventName); if (listeners.length === 0) { this.debug(`A lifecycle event with the name ${eventName} does not exist. An event must be registered before it can be emitted.`); } else { for (const cb of listeners) { await cb(data); } } } } exports.Lifecycle = Lifecycle; //# sourceMappingURL=lifecycleEvents.js.map