Search code examples
angularprogressive-web-appsservice-workerangular-service-worker

Angular 9 PWA: Service Worker / Manifest scope issues when running app in subdirectory and service worker hosted in a different subdirectory


Using ASP.NET CORE 3.1 to host and serve everything. "index.html" is not intended to be accessed directly, "/app" hits an endpoint in .net core that serves the index.html file

  1. Angular 9 with PWA
  2. Running app at https://example.com/app
  3. Service worker and assets are stored at https://example.com/dist/assets
  4. Scope of service worker was set to "/"
  5. manifest scope set to "/" and start_url set to "index.html"

Problems:

  1. Service worker is trying to cache assets at root: https://example.com/index.html. SHOULD BE https://example.com/dist/assets/index.html
  2. I tried catching this on the server, and setting 301 permanent redirect to the correct asset
  3. The problem is, I also have an endpoint Home/Logout which the service worker is trying to serve up
  4. I also have code on the backend that, instead of serving a 404 for unfound paths, it just serves the result of hitting /app
  5. If you access index.html but you are not logged in, it tries redirecting you to Home/Logout
  6. The service worker seems to think Home/Logout should be serving the content for index.html
  7. You end up in this crazy infinite redirect loop

So, the root problem has to do with the app running at /app instead of /, and potentially having all of my assets at /dist/assets instead of /.

I need the PWA to be installable from my landing page, located at /, but the start_url would ideally be "/app", but as long as it is the index.html file that is fine

Is there any configuration changes I could make, or any .net core changes I could make, to support this?

Also, I did not install the pwa with a basehref parameter if that matters.


Solution

  • I figured it out.

    1. You want to build with --baseRef=/your/sub/directory/here/
    2. You want your manifest file to have scope="." and start_url="./"
    3. You need to update your ngsw-config.json file.
    4. That means, add navigationUrls property, include everything angular provides in stock form, then add rules to EXCLUDE specific urls.
    5. Angular provides the following in stock form:

        [
          '/**',           
          '!/**/*.*',      
          '!/**/*__*',     
          '!/**/*__*/**',  
        ] 

    1. In my case, I needed to exclude urls that contain "Home". But, there doesn't seem to be a glob pattern I can use that works with angular's compiler. So, I wrote a deploy step that merges a custom configuration with the configuration that angular compiles.

            public static void Execute()
            {
                Log.Info("Configuring ngsw.json");
                var ngsw = System.IO.File.ReadAllText($"{DeploySettings.SourcePath}wwwroot/dist/assets/ngsw.json");
    
                var ngswJson = JObject.Parse(ngsw);
                var toMerge = JObject.Parse(@"{
                                                ""navigationUrls"":[
                                                    {
                                                      ""positive"": false,
                                                      ""regex"": ""^.*Home.*$""
                                                    }
    
                                                 ]}");
                ngswJson.Merge(toMerge);
    
                var json = ngswJson.ToString();
    
                System.IO.File.WriteAllText($"{DeploySettings.SourcePath}wwwroot/dist/assets/ngsw.json", json);
            }

    At this point, I just have to figure out how to exclude my landing page and we'll be good. My app works offline finally!