Home Reference Source Repository

src/component/cache-component.js

'use strict';

const npmEvents = require('./npm/events');
const CacheFactory = require('./cache/factory');
const DependencyBasedComponent = require('./dependency-based-component');
const Spinner = require('./helper/spinner');
const cacheEvents = require('./cache/events');
const prettyBytes = require('pretty-bytes');
const SequentialPromise = require('./helper/sequential-promise');

/**
 * Cache component
 */
class CacheComponent extends DependencyBasedComponent {
  /**
   * @param {*} args
   */
  constructor(...args) {
    super(...args);
    
    this._caches = [];
  }
  
  /**
   * @returns {string}
   */
  get name() {
    return 'cache';
  }
  
  /**
   * @returns {string[]}
   */
  get dependencies() {
    return [];
  }

  /**
   * @returns {AbstractDriver[]}
   */
  get caches() {
    return this._caches;
  }

  /**
   * @param {Emitter} emitter
   * 
   * @returns {Promise}
   */
  init(emitter) {
    return Promise.all([
      this._initCaches(emitter),
      this._initNpmCache(emitter)
    ]);
  }

  /**
   * @param {Emitter} emitter
   * 
   * @returns {Promise}
   */
  teardown(emitter) {
    if (this.caches.length <= 0) {
      return Promise.resolve();
    }

    return SequentialPromise.all(this.caches.map(cache => {
      return () => this._uploadCache(cache);
    }));
  }

  /**
   * @param {Emitter} emitter
   * 
   * @returns {Promise}
   * 
   * @private
   */
  _initCaches(emitter) {
    const cachePaths = this.container.get('paths', []);

    if (cachePaths.length <= 0) {
      return Promise.resolve();
    }

    return SequentialPromise.all(cachePaths.map(cacheDir => {
      return () => {
        const cache = this._createCache(cacheDir);

        this.caches.push(cache);

        return this._downloadCache(cache);
      };
    }));
  }

  /**
   * @param {Emitter} emitter
   * 
   * @returns {Promise}
   * 
   * @private
   */
  _initNpmCache(emitter) {
    const enabled = this.container.get('npm', true);

    if (!enabled || !emitter.component('npm')) {
      return Promise.resolve();
    }

    emitter.onBlocking(npmEvents.npm.cache.init, cacheDir => {
      const npmCache = this._createCache(cacheDir);

      this.caches.push(npmCache);

      return this._downloadCache(npmCache);
    });

    return Promise.resolve();
  }

  /**
   * @param {string} cacheDir
   *
   * @returns {AbstractDriver}
   *
   * @private
   */
  _createCache(cacheDir) {
    return CacheFactory.create(
      this.container.get('driver'),
      cacheDir,
      ...this.container.get('options', [])
    );
  }

  /**
   * @param {AbstractDriver} cache 
   * 
   * @returns {Promise}
   * 
   * @private
   */
  _downloadCache(cache) {
    const spinner = new Spinner(`Downloading caches from #${ cache.name }`);
      
    return spinner.then(
      `Caches downloaded to ${ cache.cacheDir }`
    ).catch(
      `Failed to download caches to ${ cache.cacheDir }`
    ).promise(
      this._trackProgress(cache, spinner).download()
    );
  }

  /**
   * @param {AbstractDriver} cache 
   * 
   * @returns {Promise}
   * 
   * @private
   */
  _uploadCache(cache) {
    const spinner = new Spinner(`Uploading caches to #${ cache.name }`);
      
    return spinner.then(
      `Caches uploaded from ${ cache.cacheDir }`
    ).catch(
      `Failed to upload caches from ${ cache.cacheDir }`
    ).promise(
      this._trackProgress(cache, spinner).upload()
    );
  }
  
  /**
   * @param {EventEmitter} emitter
   * @param {ora} spinner
   *
   * @returns {EventEmitter}
   *
   * @private
   */
  _trackProgress(emitter, spinner) {
    emitter.on(cacheEvents.cache.upload.progress, payload => {
      const { amount, total } = payload;
      
      spinner.prepend(`[${ prettyBytes(amount) }/${ prettyBytes(total) }]`);
    });
    
    spinner.prepend(`[0/0]`);
    
    return emitter;
  }
}

module.exports = CacheComponent;