Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ng serve does not allow us access to raw 'index.csr.html' when using SSR #28063

Closed
1 task
DibyodyutiMondal opened this issue Jul 17, 2024 · 4 comments · Fixed by #28069
Closed
1 task

ng serve does not allow us access to raw 'index.csr.html' when using SSR #28063

DibyodyutiMondal opened this issue Jul 17, 2024 · 4 comments · Fixed by #28069

Comments

@DibyodyutiMondal
Copy link

Command

serve

Is this a regression?

  • Yes, this behavior used to work in the previous version

The previous version in which this bug was not present was

No response

Description

If I go to http://localhost:4200/, I get a transformed html response which includes my lazy-loaded router-component for the ''path. This is as expected during SSR.

However,
If I go to http://localhost:4200/index.html, I expect to get an html response which is exactly the index.html on disk, without the ssr transformation. But the dev server sends me a "404 Not Found" instead.

Turns out that, for SSR, angular creates 2 index.html - index.csr.html and index.server.html. I am interested in the csr version, because I want to precache it.

However, the angular dev server's middleware prevents me from accessing that file.

image

This is a screenshot of me debugging the dev-server. The request was for /index.html

In line 25, the middleware tries to search the outputFiles for 'index.html'. The problem is that the outputFile don't have 'index.html'. They have 'index.csr.html' and 'index.server.html'. As a result, rawHtml is going to be always be undefined, and the middleware is going to go to line 27 and skip the middleware.

If I try to go for /index.csr.html, then the middleware will skip out before it even reaches Line 25, due to the if condition on Line 12
Line 12 says that any request that are not '/' or '/index.html' will be ignored.

So we are perpetually denied being served the raw index.html while serving in SSR

Minimal Reproduction

Create a new angular app
Add SSR to it as per the documentation
Add a lazy loaded component for the path '' with the selector test-element and an empty template,
Remove the contents of app.component's template and add a router outlet to app.component.html
Do other task necessary to set up routing
Serve the app with ng serve

Go to http://localhost:4200 and check the response that came on first load. You should see the element <test-element></test-element> in the html. This is ok

Go to http://localhost:4200/index.html. This should result in 404 Not Found. This is ok.

Go to http://localhost:4200/index.csr.html. This should result in 404 Not Found. This is not ok. It should have returned html without the test-element in it.

Exception or Error

No response

Your Environment

Angular CLI: 18.1.0
Node: 20.13.1
Package Manager: pnpm 9.4.0
OS: linux x64

Angular: 18.1.0
... animations, cdk, cli, common, compiler, compiler-cli, core
... forms, language-service, material, material-luxon-adapter
... platform-browser, platform-browser-dynamic, platform-server
... router, ssr

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1702.3
@angular-devkit/build-angular   18.1.0
@angular-devkit/core            18.1.0
@angular-devkit/schematics      18.1.0
@schematics/angular             18.1.0
ng-packagr                      18.1.0
rxjs                            7.8.1
typescript                      5.4.4

Anything else relevant?

const pathname = pathnameWithoutBasePath(req.url, server.config.base);
if (pathname !== '/' && pathname !== '/index.html') {
next();
return;
}
const rawHtml = outputFiles.get('/index.html')?.contents;
if (!rawHtml) {
next();
return;
}

This is the part that likely needs to be changed.

  • change line 31 to allow index.csr.html
  • change line 37 to search for index.csr.html or index.html as per the actual request
@DibyodyutiMondal
Copy link
Author

DibyodyutiMondal commented Jul 18, 2024

Workaround:

Explanation

During ngOnInit check the route for the existence for a query parameter called __blank_page. If it is present, don't render any routes. Then, wherever you want to cache your empty app shell, use /__blank_page for the url, instead of index.html/index.csr.html

Implementation

In your app.component.ts:

  protected readonly blankPage$ = signal(false);

  private serveBlankPage() {
    this.route.queryParamMap
      .pipe(takeUntilDestroyed(this.dRef))
      .subscribe(params => {
        if (params.has('__blank_page')) {
          console.debug(params);
          this.blankPage$.set(true);
        }
      });
  }

  ngOnInit() {
    this.serveBlankPage();
  }

In your app.component.html:

     <header></header>
    @if (!blankPage$()) {
    <router-outlet></router-outlet>
    }
    <footer></footer>

Finally, for precaching, use the url: /?__blank_page in your precaching manifest

Drawbacks

You can only wrap the router outlet in the @if block. While it is possible to wrap the entire template in the @if, this will interfere with any viewchild.required declarations in your typescript. If you are willing to refactor your typescript to accommodate that, then this is a viable temporary solution

While you can choose to make the

alan-agius4 added a commit to alan-agius4/angular-cli that referenced this issue Jul 18, 2024
Ensure direct requests to HTML files result in them being served.

Closes angular#28063
@alan-agius4
Copy link
Collaborator

Can you please elaborate on what you mean by "precaching" and what you are trying to achieve with it?

alan-agius4 added a commit to alan-agius4/angular-cli that referenced this issue Jul 19, 2024
Ensure direct requests to HTML files result in them being served.

Closes angular#28063
@clydin clydin closed this as completed in 11a140b Jul 19, 2024
clydin pushed a commit that referenced this issue Jul 19, 2024
Ensure direct requests to HTML files result in them being served.

Closes #28063

(cherry picked from commit 11a140b)
@DibyodyutiMondal
Copy link
Author

DibyodyutiMondal commented Jul 20, 2024

@alan-agius4 Sorry for the delay in getting back.

So, I use a service worker, and instead of @angular/pwa, I use workbox to implement a custom service worker.

And workbox has the ability to "pre-cache" certain files to ensure that the application always loads, even if offline. To do this, it loads certain files up-front at the time of installation. Usually, this would mean all files in the 'initial' chunk + the index.html file + global styles + polyfills. So for those items, I would need the file names in the build output, then make a list, and pass that to the service worker.

The service worker would then, at install-time, eagerly cache all the files in the list by making a network request. In the case of index.html/index.csr.html, it was returning a 404, and the service worker installation would error out. If used / instead of index.html, it would receive from the server and cache SSR-generated page for the home page (which our default route). If I went offline at this stage, changing routes would cause a very annoying flicker on every navigation, as it would first render the app-component + home-page component, then replace the home-page component with whatever component I am routing to (or fallback to NotFoundComponent). Using the un-altered index.csr.html would get rid of that layout jump in offline scenarios.

https://developer.chrome.com/docs/workbox/modules/workbox-precaching

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Aug 20, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants