"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.UIServer = void 0;
function _lodash() {
  const data = require("lodash");
  _lodash = function () {
    return data;
  };
  return data;
}
function _express() {
  const data = _interopRequireDefault(require("express"));
  _express = function () {
    return data;
  };
  return data;
}
function _expressHistoryApiFallback() {
  const data = _interopRequireDefault(require("express-history-api-fallback"));
  _expressHistoryApiFallback = function () {
    return data;
  };
  return data;
}
function _toolboxNetwork() {
  const data = require("@teambit/toolbox.network.get-port");
  _toolboxNetwork = function () {
    return data;
  };
  return data;
}
function _utils() {
  const data = require("@teambit/legacy/dist/utils");
  _utils = function () {
    return data;
  };
  return data;
}
function _httpProxy() {
  const data = _interopRequireDefault(require("http-proxy"));
  _httpProxy = function () {
    return data;
  };
  return data;
}
function _path() {
  const data = require("path");
  _path = function () {
    return data;
  };
  return data;
}
function _webpack() {
  const data = _interopRequireDefault(require("webpack"));
  _webpack = function () {
    return data;
  };
  return data;
}
function _webpackDevServer() {
  const data = _interopRequireDefault(require("webpack-dev-server"));
  _webpackDevServer = function () {
    return data;
  };
  return data;
}
function _ssrMiddleware() {
  const data = require("./ssr-middleware");
  _ssrMiddleware = function () {
    return data;
  };
  return data;
}
function _ui() {
  const data = require("./ui.aspect");
  _ui = function () {
    return data;
  };
  return data;
}
function _webpackDev() {
  const data = require("./webpack/webpack.dev.config");
  _webpackDev = function () {
    return data;
  };
  return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : String(i); }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
class UIServer {
  constructor(graphql, expressExtension, ui, uiRoot, uiRootExtension, logger, publicDir, plugins) {
    this.graphql = graphql;
    this.expressExtension = expressExtension;
    this.ui = ui;
    this.uiRoot = uiRoot;
    this.uiRootExtension = uiRootExtension;
    this.logger = logger;
    this.publicDir = publicDir;
    this.plugins = plugins;
    _defineProperty(this, "_port", 0);
    _defineProperty(this, "setReady", void 0);
    _defineProperty(this, "startPromise", new Promise(resolve => this.setReady = resolve));
  }
  getName() {
    return this.uiRoot.name;
  }
  get port() {
    return this._port;
  }

  /** the hostname for the server to listen at. Currently statically 'localhost' */
  get host() {
    return 'localhost';
  }

  /** the server listens at this url */
  get fullUrl() {
    const port = this.port !== 80 ? `:${this.port}` : '';
    return `http://${this.host}${port}`;
  }
  get buildOptions() {
    return this.uiRoot.buildOptions;
  }

  /**
   * get the webpack configuration of the UI server.
   */
  async getDevConfig() {
    const aspects = await this.uiRoot.resolveAspects(_ui().UIRuntime.name);
    return (0, _webpackDev().devConfig)(this.uiRoot.path, [await this.ui.generateRoot(aspects, this.uiRootExtension)], this.uiRoot.name);
  }
  get whenReady() {
    return Promise.all([this.startPromise, ...this.plugins.map(x => x?.whenReady)]);
  }

  /**
   * start a UI server.
   */
  async start({
    bundleUiRoot,
    portRange
  } = {}) {
    const app = this.expressExtension.createApp();
    const publicDir = `/${this.publicDir}`;
    const defaultRoot = (0, _path().join)(this.uiRoot.path, publicDir);
    const root = bundleUiRoot || defaultRoot;
    this.logger.debug(`UiServer, start from ${root}`);
    const server = await this.graphql.createServer({
      app
    });

    // set up proxy, for things like preview, e.g. '/preview/teambit.react/react'
    await this.configureProxy(app, server);

    // pass through files from public /folder:
    // setting `index: false` so index.html will be served by the fallback() middleware
    app.use(_express().default.static(root, {
      index: false
    }));
    const port = await _toolboxNetwork().Port.getPortFromRange(portRange || [3100, 3200]);
    await this.setupServerSideRendering({
      root,
      port,
      app
    });

    // in any and all other cases, serve index.html.
    // No any other endpoints past this will execute
    app.use((0, _expressHistoryApiFallback().default)('index.html', {
      root
    }));
    server.listen(port);
    this._port = port;

    // important: we use the string of the following message for the http.e2e.ts. if you change the message,
    // please make sure you change the `HTTP_SERVER_READY_MSG` const.
    this.logger.info(`UI server of ${this.uiRootExtension} is listening to port ${port}`);
    this.setReady();
  }
  getPluginsComponents() {
    return this.plugins.map(plugin => plugin.render);
  }
  async setupServerSideRendering({
    root,
    port,
    app
  }) {
    if (!this.buildOptions?.ssr) return;
    const ssrMiddleware = await (0, _ssrMiddleware().createSsrMiddleware)({
      root,
      port,
      title: this.uiRoot.name,
      logger: this.logger
    });
    if (!ssrMiddleware) {
      this.logger.warn('[ssr] middleware failed setup');
      return;
    }

    // eslint-disable-next-line @typescript-eslint/no-misused-promises
    app.get('*', ssrMiddleware);
    this.logger.debug('[ssr] serving for "*"');
  }
  async configureProxy(app, server) {
    const proxServer = _httpProxy().default.createProxyServer();
    proxServer.on('error', e => this.logger.error(e.message));
    const proxyEntries = await this.getProxyFromPlugins();

    // TODO - should use https://github.com/chimurai/http-proxy-middleware
    server.on('upgrade', function (req, socket, head) {
      const entry = proxyEntries.find(proxy => proxy.context.some(item => item === (0, _utils().stripTrailingChar)(req.url, '/')));
      if (!entry) return;
      proxServer.ws(req, socket, head, {
        target: entry.target
      });
    });
    proxyEntries.forEach(entry => {
      entry.context.forEach(route => {
        app.use(`${route}/*`, (req, res) => {
          req.url = req.originalUrl;
          proxServer.web(req, res, entry);
        });
      });
    });
  }

  /**
   * start a UI dev server.
   */
  async dev({
    portRange
  } = {}) {
    const devServerPort = await this.selectPort(portRange);
    await this.start({
      portRange: [4100, 4200]
    });
    const expressAppPort = this._port;
    const config = await this.getDevConfig();
    const compiler = (0, _webpack().default)(config);
    const devServerConfig = await this.getDevServerConfig(devServerPort, expressAppPort, config.devServer);
    // @ts-ignore in the capsules it throws an error about compatibilities issues between webpack.compiler and webpackDevServer/webpack/compiler
    const devServer = new (_webpackDevServer().default)(devServerConfig, compiler);
    await devServer.start();
    this._port = devServerPort;
    return devServer;
  }
  async selectPort(portRange) {
    return _toolboxNetwork().Port.getPortFromRange(portRange || [3100, 3200]);
  }
  async getProxyFromPlugins() {
    const proxiesByPlugin = this.plugins.map(plugin => {
      return plugin.getProxy ? plugin.getProxy() : [];
    });
    return (0, _lodash().flatten)(await Promise.all(proxiesByPlugin));
  }
  async getProxy(port = 4000) {
    const proxyEntries = await this.getProxyFromPlugins();
    const gqlProxies = [{
      context: ['/graphql', '/api'],
      target: `http://${this.host}:${port}`,
      changeOrigin: true
    }, {
      context: ['/subscriptions'],
      target: `ws://${this.host}:${port}`,
      ws: true
    }];
    return gqlProxies.concat(proxyEntries);
  }
  async getDevServerConfig(appPort, gqlPort, config) {
    const proxy = await this.getProxy(gqlPort);
    const devServerConf = _objectSpread(_objectSpread({}, config), {}, {
      proxy,
      port: appPort
    });
    return devServerConf;
  }
  static create(props) {
    return new UIServer(props.graphql, props.express, props.ui, props.uiRoot, props.uiRootExtension, props.logger, props.publicDir, props.startPlugins);
  }
}
exports.UIServer = UIServer;

//# sourceMappingURL=ui-server.js.map