watch.js

/* @flow */

import type {WatchCallback} from './typedef';
import {logError, log, consoleStyles} from './logger';
import {Client} from 'fb-watchman';

/* eslint-disable camelcase */

const client = new Client(),
  ALPHANUMERIC_BASE = 36,
  {yellow} = consoleStyles;

/**
 * Using the [Facebook Watchman](https://facebook.github.io/watchman/), watches the directory `dir` for changes of files
 * with extension `type` and runs `callback` when a change is detected.
 *
 * This watcher's only goal is performance, hence the simplicity.
 *
 * @function watch
 * @param {string}        dir      - a full system path to a directory to watch
 * @param {string}        type     - a file extension
 * @param {WatchCallback} callback - a callback function
 * @see {@link https://facebook.github.io/watchman/ Watchman}
 * @example
 * import {watch} from 'webcompiler';
 * // or - import {watch} from 'webcompiler/lib/watch';
 * // or - var watch = require('webcompiler').watch;
 * // or - var watch = require('webcompiler/lib/watch').watch;
 * import {join} from 'path';
 *
 * watch(join(__dirname, 'src'), 'js', someFunction);
 */
export function watch(dir: string, type: string, callback: WatchCallback) {
  const subscription = Date.now().toString(ALPHANUMERIC_BASE);

  client.capabilityCheck({optional: [], required: ['relative_root']}, capabilityErr => {
    if (capabilityErr) {
      client.end();

      return logError(capabilityErr);
    }

    client.command(['watch-project', dir], (watchErr, {watch: watcher, relative_path: relative_root, warning}) => {
      if (watchErr) {
        return logError(watchErr);
      }

      if (warning) {
        log(yellow('Warning: ', warning));
      }

      client.command(['clock', watcher], (clockErr, {clock: since}) => {
        if (clockErr) {
          return logError(clockErr);
        }

        client.command(['subscribe', watcher, subscription, {
          expression: ['suffix', type],
          since,
          relative_root
        }], subscribeErr => {
          if (subscribeErr) {
            logError(subscribeErr);
          }
        });

        client.on('subscription', subscriptionResp => {
          if (subscription === subscriptionResp.subscription) {
            callback(subscriptionResp);
          }
        });
      });
    });
  });
}