"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = exports.WATCHER_COMPLETED_MSG = exports.STARTED_WATCHING_MSG = void 0;

function _bluebird() {
  const data = require("bluebird");

  _bluebird = function () {
    return data;
  };

  return data;
}

function _defineProperty2() {
  const data = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));

  _defineProperty2 = function () {
    return data;
  };

  return data;
}

function _chokidar() {
  const data = _interopRequireDefault(require("chokidar"));

  _chokidar = function () {
    return data;
  };

  return data;
}

function _ramda() {
  const data = _interopRequireDefault(require("ramda"));

  _ramda = function () {
    return data;
  };

  return data;
}

function _chalk() {
  const data = _interopRequireDefault(require("chalk"));

  _chalk = function () {
    return data;
  };

  return data;
}

function _consumer() {
  const data = _interopRequireDefault(require("../consumer"));

  _consumer = function () {
    return data;
  };

  return data;
}

function _() {
  const data = require("..");

  _ = function () {
    return data;
  };

  return data;
}

function _consumer2() {
  const data = require("../../api/consumer");

  _consumer2 = function () {
    return data;
  };

  return data;
}

function _loader() {
  const data = _interopRequireDefault(require("../../cli/loader"));

  _loader = function () {
    return data;
  };

  return data;
}

function _bitId() {
  const data = require("../../bit-id");

  _bitId = function () {
    return data;
  };

  return data;
}

function _constants() {
  const data = require("../../constants");

  _constants = function () {
    return data;
  };

  return data;
}

function _utils() {
  const data = require("../../utils");

  _utils = function () {
    return data;
  };

  return data;
}

/* eslint no-console: 0 */
const STARTED_WATCHING_MSG = 'started watching for component changes to rebuild';
exports.STARTED_WATCHING_MSG = STARTED_WATCHING_MSG;
const WATCHER_COMPLETED_MSG = 'watching for changes';
exports.WATCHER_COMPLETED_MSG = WATCHER_COMPLETED_MSG;

class WatchComponents {
  // dir => component-id
  constructor(verbose) {
    (0, _defineProperty2().default)(this, "trackDirs", {});
    this.verbose = verbose;
  }

  watchAll() {
    var _this = this;

    return (0, _bluebird().coroutine)(function* () {
      // TODO: run build in the beginning of process (it's work like this in other envs)
      _this.consumer = yield (0, _().loadConsumer)();

      const watcher = _this._getWatcher();

      console.log(_chalk().default.yellow(`bit binary version: ${_constants().BIT_VERSION}`));
      console.log(_chalk().default.yellow(`node version: ${process.version}`));
      const log = console.log.bind(console);
      return new Promise((resolve, reject) => {
        // prefix your command with "BIT_LOG=*" to see all watch events
        if (process.env.BIT_LOG) {
          watcher.on('all', (event, path) => {
            log(event, path);
          });
        }

        watcher.on('ready', () => {
          log(_chalk().default.yellow(STARTED_WATCHING_MSG));
        });
        watcher.on('change', p => {
          log(`file ${p} has been changed`);

          _this._handleChange(p).catch(err => reject(err));
        });
        watcher.on('add', p => {
          log(`file ${p} has been added`);

          _this._handleChange(p, true).catch(err => reject(err));
        });
        watcher.on('unlink', p => {
          log(`file ${p} has been removed`);

          _this._handleChange(p).catch(err => reject(err));
        });
        watcher.on('error', err => {
          log(`Watcher error ${err}`);
          reject(err);
        });
      });
    })();
  }

  _handleChange(filePath, isNew = false) {
    var _this2 = this;

    return (0, _bluebird().coroutine)(function* () {
      const componentId = yield _this2._getBitIdByPathAndReloadConsumer(filePath, isNew);

      if (componentId) {
        const idStr = componentId.toString();
        console.log(`running build for ${_chalk().default.bold(idStr)}`); // TODO: Make sure the log for build is printed to console

        const buildResults = yield (0, _consumer2().build)(idStr, false, _this2.verbose);

        if (buildResults) {
          console.log(`\t${_chalk().default.cyan(buildResults.join('\n\t'))}`);
        } else {
          console.log(`${idStr} doesn't have a compiler, nothing to build`);
        }
      } else {
        console.log(`file ${filePath} is not part of any component, ignoring it`);
      }

      _loader().default.stop();

      console.log(_chalk().default.yellow(WATCHER_COMPLETED_MSG));
    })();
  }

  _getBitIdByPathAndReloadConsumer(filePath, isNew) {
    var _this3 = this;

    return (0, _bluebird().coroutine)(function* () {
      const relativeFile = (0, _utils().pathNormalizeToLinux)(_this3.consumer.getPathRelativeToConsumer(filePath));

      let componentId = _this3.consumer.bitMap.getComponentIdByPath(relativeFile);

      if (!isNew && !componentId) {
        return null;
      }

      _this3.consumer = yield (0, _().loadConsumer)(undefined, true);

      if (!componentId) {
        componentId = _this3.consumer.bitMap.getComponentIdByPath(relativeFile);
      }

      if (isNew && !componentId) {
        const trackDir = Object.keys(_this3.trackDirs).find(dir => relativeFile.startsWith(dir));

        if (trackDir) {
          const id = _this3.trackDirs[trackDir];

          const bitId = _this3.consumer.getParsedId(id); // loading the component causes the bitMap to be updated with the new path


          yield _this3.consumer.loadComponent(bitId);
          componentId = _this3.consumer.bitMap.getComponentIdByPath(relativeFile);
        }
      }

      return componentId;
    })();
  }

  _getWatcher() {
    const pathsToWatch = this._getPathsToWatch();

    return _chokidar().default.watch(pathsToWatch, {
      ignoreInitial: true,
      // Using the function way since the regular way not working as expected
      // It might be solved when upgrading to chokidar > 3.0.0
      // See:
      // https://github.com/paulmillr/chokidar/issues/773
      // https://github.com/paulmillr/chokidar/issues/492
      // https://github.com/paulmillr/chokidar/issues/724
      ignored: path => {
        // Ignore package.json temporarily since it cerates endless loop since it's re-written after each build
        if (path.includes('dist') || path.includes('node_modules') || path.includes('package.json')) {
          return true;
        }

        return false;
      },
      persistent: true,
      useFsEvents: false
    });
  }

  _getPathsToWatch() {
    const componentsFromBitMap = this.consumer.bitMap.getAllComponents();
    const paths = Object.keys(componentsFromBitMap).map(componentId => {
      const componentMap = componentsFromBitMap[componentId];
      const trackDir = componentMap.getTrackDir();

      if (trackDir) {
        this.trackDirs[trackDir] = componentId;
      }

      const relativePaths = trackDir ? [trackDir] : componentMap.getFilesRelativeToConsumer();
      const absPaths = relativePaths.map(relativePath => this.consumer.toAbsolutePath(relativePath));

      if (this.verbose) {
        console.log(`watching ${_chalk().default.bold(componentId)}\n${absPaths.join('\n')}`);
      }

      return absPaths;
    });
    return _ramda().default.flatten(paths);
  }

}

exports.default = WatchComponents;