async function initBrowserSync()

in modules/builders/src/ssr-dev-server/index.ts [202:308]


async function initBrowserSync(
  browserSyncInstance: browserSync.BrowserSyncInstance,
  nodeServerPort: number,
  options: SSRDevServerBuilderOptions,
  context: BuilderContext,
): Promise<browserSync.BrowserSyncInstance> {
  if (browserSyncInstance.active) {
    return browserSyncInstance;
  }

  const { port: browserSyncPort, open, host, publicHost, proxyConfig } = options;
  const bsPort = browserSyncPort || (await getAvailablePort());
  const bsOptions: browserSync.Options = {
    proxy: {
      target: `localhost:${nodeServerPort}`,
      proxyOptions: {
        xfwd: true,
      },
      proxyRes: [
        (proxyRes) => {
          if ('headers' in proxyRes) {
            proxyRes.headers['cache-control'] = undefined;
          }
        },
      ],
      // proxyOptions is not in the typings
    } as browserSync.ProxyOptions & { proxyOptions: { xfwd: boolean } },
    host,
    port: bsPort,
    ui: false,
    server: false,
    notify: false,
    ghostMode: false,
    logLevel: 'silent',
    open,
    https: getSslConfig(context.workspaceRoot, options),
  };

  const publicHostNormalized =
    publicHost && publicHost.endsWith('/')
      ? publicHost.substring(0, publicHost.length - 1)
      : publicHost;

  if (publicHostNormalized) {
    const { protocol, hostname, port, pathname } = url.parse(publicHostNormalized);
    const defaultSocketIoPath = '/browser-sync/socket.io';
    const defaultNamespace = '/browser-sync';
    const hasPathname = !!(pathname && pathname !== '/');
    const namespace = hasPathname ? pathname + defaultNamespace : defaultNamespace;
    const path = hasPathname ? pathname + defaultSocketIoPath : defaultSocketIoPath;

    bsOptions.socket = {
      namespace,
      path,
      domain: url.format({
        protocol,
        hostname,
        port,
      }),
    };

    // When having a pathname we also need to create a reverse proxy because socket.io
    // will be listening on: 'http://localhost:4200/ssr/browser-sync/socket.io'
    // However users will typically have a reverse proxy that will redirect all matching requests
    // ex: http://testinghost.com/ssr -> http://localhost:4200 which will result in a 404.
    if (hasPathname) {
      // Remove leading slash
      // eslint-disable-next-line @typescript-eslint/no-unused-expressions
      (bsOptions.scriptPath = (p) => p.substring(1)),
        (bsOptions.middleware = [
          createProxyMiddleware(defaultSocketIoPath, {
            target: url.format({
              protocol: 'http',
              hostname: host,
              port: bsPort,
              pathname: path,
            }),
            ws: true,
            logLevel: 'silent',
          }) as any,
        ]);
    }
  }

  if (proxyConfig) {
    if (!bsOptions.middleware) {
      bsOptions.middleware = [];
    } else if (!Array.isArray(bsOptions.middleware)) {
      bsOptions.middleware = [bsOptions.middleware];
    }

    bsOptions.middleware = [
      ...bsOptions.middleware,
      ...getProxyConfig(context.workspaceRoot, proxyConfig),
    ];
  }

  return new Promise((resolve, reject) => {
    browserSyncInstance.init(bsOptions, (error, bs) => {
      if (error) {
        reject(error);
      } else {
        resolve(bs);
      }
    });
  });
}