lib/tasks/serve.js

'use strict';

const fs = require('fs');
const path = require('path');
const ExpressServer = require('./server/express-server');
const Task = require('../models/task');
const Watcher = require('../models/watcher');
const ServerWatcher = require('../models/server-watcher');
const Builder = require('../models/builder');
const SilentError = require('silent-error');
const serveURL = require('../utilities/get-serve-url');
const pDefer = require('p-defer');

function mockWatcher(distDir) {
  let watcher = Promise.resolve({ directory: distDir });
  watcher.on = () => {};
  return watcher;
}

function mockBuilder() {
  return {
    cleanup: () => Promise.resolve(),
  };
}

class ServeTask extends Task {
  constructor(options) {
    super(options);

    this._runDeferred = null;
    this._builder = null;
  }

  async run(options) {
    let hasBuild = !!options.path;

    if (hasBuild) {
      if (!fs.existsSync(options.path)) {
        throw new SilentError(
          `The path ${options.path} does not exist. Please specify a valid build directory to serve.`
        );
      }
      options._builder = mockBuilder();
      options._watcher = mockWatcher(options.path);
    }

    let builder = (this._builder =
      options._builder ||
      new Builder({
        ui: this.ui,
        outputPath: options.outputPath,
        project: this.project,
        environment: options.environment,
      }));

    let watcher =
      options._watcher ||
      (
        await Watcher.build({
          ui: this.ui,
          builder,
          options,
          serving: true,
          ignored: [path.resolve(this.project.root, options.outputPath)],
        })
      ).watcher;

    let serverRoot = './server';
    let serverWatcher = null;
    if (fs.existsSync(serverRoot)) {
      serverWatcher = (
        await ServerWatcher.build({
          ui: this.ui,
          watchedDir: path.resolve(serverRoot),
          options,
        })
      ).watcher;
    }

    let expressServer =
      options._expressServer ||
      new ExpressServer({
        ui: this.ui,
        project: this.project,
        watcher,
        serverRoot,
        serverWatcher,
      });

    /* hang until the user exits */
    this._runDeferred = pDefer();

    await expressServer.start(options);

    if (hasBuild) {
      this.ui.writeLine(`– Serving on ${serveURL(options, this.project)}`);
    }

    return this._runDeferred.promise;
  }

  /**
   * Exit silently
   *
   * @private
   * @method onInterrupt
   */
  async onInterrupt() {
    await this._builder.cleanup();
    return this._runDeferred.resolve();
  }
}

module.exports = ServeTask;