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),
}),
),
);
}