Repository: angular/angular-cli
File: packages/angular/ssr/src/utils/ng.ts (TypeScript)

Code before:
      const moduleRef = await platformRef.bootstrapModule(bootstrap);
      applicationRef = moduleRef.injector.get(ApplicationRef);
    } else {
      applicationRef = await bootstrap({ platformRef });
    }

    // Block until application is stable.
    await applicationRef.whenStable();

    // TODO(alanagius): Find a way to avoid rendering here especially for redirects as any output will be discarded.
    const envInjector = applicationRef.injector;
    const routerIsProvided = !!envInjector.get(ActivatedRoute, null);
    const router = envInjector.get(Router);
    const lastSuccessfulNavigation = router.lastSuccessfulNavigation();

    if (!routerIsProvided) {
      hasNavigationError = false;
    } else if (lastSuccessfulNavigation?.finalUrl) {
      hasNavigationError = false;

      const baseHref =
        envInjector.get(APP_BASE_HREF, null, { optional: true }) ??
        envInjector.get(PlatformLocation).getBaseHrefFromDOM();
      const finalUrl = lastSuccessfulNavigation.finalUrl;
      const finalUrlSerialized = envInjector.get(UrlSerializer).serialize(finalUrl);
      const finalUrlWithBaseHref = joinUrlParts(baseHref, finalUrlSerialized);
      const initialUrl = `${urlToRender.pathname}${urlToRender.search}${urlToRender.hash}`;

      if (initialUrl !== finalUrlWithBaseHref) {
        redirectTo = finalUrlWithBaseHref;
      }
    }

    return {
      hasNavigationError,
      redirectTo,
      content: () =>
        new Promise<string>((resolve, reject) => {
          // Defer rendering to the next event loop iteration to avoid blocking, as most operations in `renderInternal` are synchronous.
          setTimeout(() => {
            renderInternal(platformRef, applicationRef)
              .then(resolve)
              .catch(reject)
              .finally(() => void asyncDestroyPlatform(platformRef));
          }, 0);
        }),
    };
  } catch (error) {
    await asyncDestroyPlatform(platformRef);

    throw error;

Review comment (suggestion):
This bit is a lot of what `Location.prepareExternalUrl` does by combining the base with the internal application path. Should we just use that? 

```
const finalUrlSerialized = envInjector.get(UrlSerializer).serialize(finalUrl);
const finalHref = new URL(Location.prepareExternalUrl(finalUrlSerialized), new URL(urlToRender).origin).href;
```

Below, the `initialUrl` comparison can't work if the base is actually a full URL and not just a path. So you'd likely want to compare the above href to `new URL(urlToRender).href`

Code after:
      const moduleRef = await platformRef.bootstrapModule(bootstrap);
      applicationRef = moduleRef.injector.get(ApplicationRef);
    } else {
      applicationRef = await bootstrap({ platformRef });
    }

    // Block until application is stable.
    await applicationRef.whenStable();

    // TODO(alanagius): Find a way to avoid rendering here especially for redirects as any output will be discarded.
    const envInjector = applicationRef.injector;
    const routerIsProvided = !!envInjector.get(ActivatedRoute, null);
    const router = envInjector.get(Router);
    const lastSuccessfulNavigation = router.lastSuccessfulNavigation();

    if (!routerIsProvided) {
      hasNavigationError = false;
    } else if (lastSuccessfulNavigation?.finalUrl) {
      hasNavigationError = false;

      const urlSerializer = envInjector.get(UrlSerializer);
      const locationStrategy = envInjector.get(LocationStrategy);
      const finalUrlSerialized = urlSerializer.serialize(lastSuccessfulNavigation.finalUrl);
      const finalExternalUrl = joinUrlParts(locationStrategy.getBaseHref(), finalUrlSerialized);

      if (urlToRender.href !== new URL(finalExternalUrl, urlToRender.origin).href) {
        redirectTo = finalExternalUrl;
      }
    }

    return {
      hasNavigationError,
      redirectTo,
      content: () =>
        new Promise<string>((resolve, reject) => {
          // Defer rendering to the next event loop iteration to avoid blocking, as most operations in `renderInternal` are synchronous.
          setTimeout(() => {
            renderInternal(platformRef, applicationRef)
              .then(resolve)
              .catch(reject)
              .finally(() => void asyncDestroyPlatform(platformRef));
          }, 0);
        }),
    };
  } catch (error) {
    await asyncDestroyPlatform(platformRef);

    throw error;
  } finally {
    if (hasNavigationError || redirectTo) {
      void asyncDestroyPlatform(platformRef);