export function execute()

in modules/builders/src/ssr-dev-server/index.ts [50:167]


export function execute(
  options: SSRDevServerBuilderOptions,
  context: BuilderContext,
): Observable<SSRDevServerBuilderOutput> {
  const browserTarget = targetFromTargetString(options.browserTarget);
  const serverTarget = targetFromTargetString(options.serverTarget);
  const getBaseUrl = (bs: browserSync.BrowserSyncInstance) =>
    `${bs.getOption('scheme')}://${bs.getOption('host')}:${bs.getOption('port')}`;
  const browserTargetRun = context.scheduleTarget(browserTarget, {
    serviceWorker: false,
    watch: true,
    progress: options.progress,
  });

  const serverTargetRun = context.scheduleTarget(serverTarget, {
    watch: true,
    progress: options.progress,
  });

  const bsInstance = browserSync.create();

  context.logger.error(tags.stripIndents`
  ****************************************************************************************
  This is a simple server for use in testing or debugging Angular applications locally.
  It hasn't been reviewed for security issues.

  DON'T USE IT FOR PRODUCTION!
  ****************************************************************************************
 `);

  return zip(browserTargetRun, serverTargetRun, getAvailablePort()).pipe(
    switchMap(([br, sr, nodeServerPort]) => {
      return combineLatest([br.output, sr.output]).pipe(
        // This is needed so that if both server and browser emit close to each other
        // we only emit once. This typically happens on the first build.
        debounceTime(120),
        switchMap(([b, s]) => {
          if (!s.success || !b.success) {
            return of([b, s]);
          }

          return startNodeServer(s, nodeServerPort, context.logger, !!options.inspect).pipe(
            mapTo([b, s]),
            catchError((err) => {
              context.logger.error(`A server error has occurred.\n${mapErrorToMessage(err)}`);

              return EMPTY;
            }),
          );
        }),
        map(
          ([b, s]) =>
            [
              {
                success: b.success && s.success,
                error: b.error || s.error,
              },
              nodeServerPort,
            ] as [SSRDevServerBuilderOutput, number],
        ),
        tap(([builderOutput]) => {
          if (builderOutput.success) {
            context.logger.info('\nCompiled successfully.');
          }
        }),
        debounce(([builderOutput]) =>
          builderOutput.success && !options.inspect
            ? waitUntilServerIsListening(nodeServerPort)
            : EMPTY,
        ),
      );
    }),
    concatMap(([builderOutput, nodeServerPort]) => {
      if (!builderOutput.success) {
        return of(builderOutput);
      }

      if (bsInstance.active) {
        bsInstance.reload();

        return of(builderOutput);
      } else {
        return from(initBrowserSync(bsInstance, nodeServerPort, options, context)).pipe(
          tap((bs) => {
            const baseUrl = getBaseUrl(bs);
            context.logger.info(tags.oneLine`
                **
                Angular Universal Live Development Server is listening on ${baseUrl},
                open your browser on ${baseUrl}
                **
              `);
          }),
          mapTo(builderOutput),
        );
      }
    }),
    map(
      (builderOutput) =>
        ({
          success: builderOutput.success,
          error: builderOutput.error,
          baseUrl: bsInstance && getBaseUrl(bsInstance),
        } as SSRDevServerBuilderOutput),
    ),
    finalize(() => {
      if (bsInstance) {
        bsInstance.exit();
        bsInstance.cleanup();
      }
    }),
    catchError((error) =>
      of({
        success: false,
        error: mapErrorToMessage(error),
      }),
    ),
  );
}