Search code examples
node.jsangularbrowser-cacheworkbox

Angular +Workbox = build ChunkLoadError: Loading chunk # and Refused to execute script because its MIME


I have added Workbox to Angular in first production deploy everything works fine, but after updating a module and rebuilding angular and injecting Workbox then visiting the site i see the service worker updates to the new version and refreshes the page, but now trying to use the updated module I get errors

Refused to execute script from 'https://example.com/8-es2015.203674bf0547eff7ff27.js'
because its MIME type ('text/html') is not executable,

and strict MIME type checking is enabled.

main-es2015.45ba4a512f87eefb1b3a.js:1 ERROR Error: Uncaught (in promise): ChunkLoadError:   
Loading chunk 8 failed.(error: https://example.com/8-es2015.203674bf0547eff7ff27.js)
ChunkLoadError: Loading chunk 8 failed......

I looked at the network in chrome and I see that the file 8-es2015.203674bf0547eff7ff27.js is being served from the (disk cache) unlike the rest of the files which get served by (ServiceWorker), its content is the index.html file I don't know where it came from its not even part of the new build ? chrome places it in top frame section under scripts

Whats the reason for this Error, in the angular.json I have "outputHashing": "all", I delete everything and rebuild but still this errors, its until I clear the browser cash remove the ServiceWorker and hard refresh that the error stops happening until I reload page and it returns. Do I need to delete all the cache after every update, I thought Workbox does this automatically.Do I add something like so in the sw.js

self.addEventListener('activate', event => event.waitUntil(
   caches.keys().then(cacheNames => cacheNames.forEach(name => caches.delete(name)))
  )
);

Am using express, so I have set the maxAge on the sw.js to 0 and even change the public route to static files to a deep route but nothing

app.use('/sw.js', express.static(path.resolve('./public/dist/static/sw.js'), {maxAge: 0}));
app.use('/', express.static(path.resolve('./public/dist/static/'), {maxAge: 86400000}));

tools: angular 8.2.4 - workbox 4.3.1

Update Removed workbox and the app worked, am guessing its cause of their new package workbox-window or the way am trying to use it. I have placed it in module service that is loaded from app.module then the service is called from a AppComponent ngOnInit. This could be the wrong way of initializing it.

code setup:

import {Workbox} from 'workbox-window';
@Injectable()
export class WorkerService {
  supportWorker: boolean;
  supportPush: boolean;    
  constructor(@Inject(WINDOW) private window: any, private loggerService: LoggerService) {
    this.supportWorker = ('serviceWorker' in navigator);
    this.supportPush = (this.supportWorker && 'PushManager' in window);
  }

  initWorker() {
    if (this.supportWorker && environment.production) {
      const wb = new Workbox('sw.js');
      if (wb) {
        wb.addEventListener('installed', event => {
          if (event.isUpdate) {
            // output a toast translated message to users
            this.loggerService.info('App.webWorkerUpdate', 10000);
            setTimeout(() => this.window.location.reload(), 10000);
          }
        });
        wb.addEventListener('activated', event => {
          if (!event.isUpdate) {
            this.loggerService.success('App.webWorkerInit', 10000);
          }
        });
        wb.register();
      }
    }
  }
}

This the app component, i thought it would be best to add it to main.ts after bootstrapModule.then() but I don't know how inject a service in this method

 @Component({
      selector: 'app-root',
      template: '<route-handler></route-handler>'
    })
    export class AppComponent implements OnInit {
      constructor(private ws: WorkerService) {
      }

      ngOnInit(): void {
        this.ws.initWorker();
      }
    }

Solution

  • After setting up Workbox in a different way it worked, the problem effected even chrome which failed to clear all cache after each build when testing it, had to use incognito to make sure everything works.

    Here is the solution thanks to Ralph Schaer article a must read. His method is not to Cache-Bust the chunks angular build generates, also he globs in all the production scripts of workbox used by the app into the build folder and finally in the index.html he calls on workbox-window to register the service-worker.