adapt-authoring-core/lib/AbstractModule.js

import Hook from './Hook.js'
/**
 * Abstract class for authoring tool modules. All custom modules must extend this class.
 * @memberof core
 */
class AbstractModule {
  /** @ignore */
  static get MODULE_READY () {
    return 'MODULE_READY'
  }

  /**
   * Create the Module instance
   * @param {Object} app Reference to the main application
   * @param {Object} pkg Config object from package.json for this module
   */
  constructor (app, pkg) {
    /** @ignore */
    this._startTime = Date.now()
    /** @ignore */
    this._isReady = false
    /**
     * Reference to the main app instance
     * @type {App}
     */
    this.app = app
    /**
     * Module config options
     * @type {Object}
     */
    this.pkg = pkg
    /**
     * Name of the module
     * @type {String}
     */
    this.name = pkg?.name || this.constructor.name
    /**
     * Root directory of this module
     * @type {String}
     */
    this.rootDir = pkg?.rootDir
    /**
     * Time taken in milliseconds for module to initialise
     * @type {Number}
     */
    this.initTime = undefined
    /**
     * Hook invoked on module ready
     * @type {Hook}
     */
    this.readyHook = new Hook()

    this.init()
      .then(() => this.setReady())
      .catch(e => this.setReady(e))
  }

  /**
   * Initialises the module. Any custom initialisation tasks should go here. Any uncaught errors thrown here will be caught later and halt the module's load, so make sure any non-fatal errors are handled.
   * @return {Promise}
   */
  async init () {
  }

  /**
   * Signals that the module is loaded
   * @param {Error} error
   * @return {Promise}
   */
  async setReady (error) {
    if (this._isReady) {
      return
    }
    await this.readyHook.invoke(error)
    if (error) {
      return
    }
    this._isReady = true
    this.initTime = Math.round((Date.now() - this._startTime))
    this.log('debug', AbstractModule.MODULE_READY, this.initTime)
  }

  /**
   * Used to listen to the module's ready signal. The returned promise will be resolved when the module has completed initialisation successfully.
   * @return {Promise}
   */
  async onReady () {
    return new Promise((resolve, reject) => {
      if (this._isReady) {
        return this._initError ? reject(this._initError) : resolve(this)
      }
      this.readyHook.tap(error => {
        /** @ignore */this._initError = error
        error ? reject(error) : resolve(this)
      })
    })
  }

  /**
   * Shortcut for retrieving config values
   * @param {String} key
   * @return {*}
   */
  getConfig (key) {
    try {
      return this.app.config.get(`${this.name}.${key}`)
    } catch (e) {
      return undefined
    }
  }

  /**
   * Log a message using the Logger module
   * @param {String} level Log level of message
   * @param {...*} rest Arguments to log
   */
  log (level, ...rest) {
    const _log = (e, instance) => {
      if (!this.app.logger || (instance && instance.name !== this.app.logger.name)) return false
      this.app.dependencyloader.moduleLoadedHook.untap(_log)
      this.app.logger.log(level, this.name.replace(/^adapt-authoring-/, ''), ...rest)
      return true
    }
    if (!_log()) this.app.dependencyloader.moduleLoadedHook.tap(_log)
  }
}

export default AbstractModule