WebPack转录的打字稿服务Worker代码似乎不起作用

发布于 2025-01-26 04:06:36 字数 26809 浏览 3 评论 0 原文

我正在尝试实现服务工作者,但如果使用WebPack + Bable将其执行服务工作者代码工作,则无法使其执行服务工作者代码的工作。注册工作正常,并且该工人被显示为激活,但是 install 也没有激活事件被触发 - 我假设由于代码未正确执行。现在,如果我用普通JavaScript中的相同代码替换了thraper的代码,则一切正常。

其他所有内容都在浏览器中正常工作,因此我的设置似乎并不是固有的问题 - 至少我不了解。我真的很想了解导致这个问题的原因!

这是相关代码和配置:

service-worker.ts

declare const self: ServiceWorkerGlobalScope;

// Incrementing OFFLINE_VERSION will kick off the install event and force
//  previously cached resources to be updated from the network.
// This variable is intentionally declared and unused.
const OFFLINE_VERSION = 1;
const CACHE_NAME = "offline";
const OFFLINE_URL = "offline.html";

self.addEventListener("install", (event) => {
  console.debug("Install service worker");

  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE_NAME);
      await cache.add(new Request(OFFLINE_URL, {cache: "reload"}));

      console.debug("Service worker installed");
    })()
  );
  void self.skipWaiting();
});

self.addEventListener("activate", (event) => {
  console.debug("Activate service worker");

  event.waitUntil(
    (async () => {
      // Enable navigation preload if it's supported.
      // See https://developers.google.com/web/updates/2017/02/navigation-preload
      if ("navigationPreload" in self.registration) {
        // @ts-ignore this is only called if the browser supports it
        await self.registration.navigationPreload.enable();

        console.debug("Service worker enabled navigationPreload");
      }
    })()
  );

  void self.clients.claim();
});

self.addEventListener("fetch", (event) => {
  if (event.request.mode === "navigate") {
    event.respondWith(
      (async () => {
        try {
          // @ts-ignore
          const preloadResponse = await event.preloadResponse;
          if (preloadResponse) {
            console.debug("Returning preloaded response for fetch request");

            return preloadResponse;
          }

          const networkResponse = await fetch(event.request);

          console.debug("Returning network response for fetch request");

          return networkResponse;
        } catch (error) {
          // catch is only triggered if an exception is thrown, which is likely
          // due to a network error.
          // If fetch() returns a valid HTTP response with a response code in
          // the 4xx or 5xx range, the catch() will NOT be called.
          console.debug(`Fetch failed; returning offline page instead: ${error}`);

          const cache = await caches.open(CACHE_NAME);
          const cachedResponse = await cache.match(OFFLINE_URL);
          return cachedResponse;
        }
      })()
    );
  } else {
    console.debug("Fetch request not handled by service worker");
  }
});

export default null;

the the the the the the service-worker.js

"use strict";
/*
 * ATTENTION: An "eval-source-map" devtool has been used.
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
(self["webpackChunkspritstat"] = self["webpackChunkspritstat"] || []).push([["service-worker"],{

/***/ "./src/service-worker.ts":
/*!*******************************!*\
  !*** ./src/service-worker.ts ***!
  \*******************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js\");\n/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/regenerator */ \"./node_modules/@babel/runtime/regenerator/index.js\");\n/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__);\n\n\n// Incrementing OFFLINE_VERSION will kick off the install event and force\n//  previously cached resources to be updated from the network.\n// This variable is intentionally declared and unused.\nvar OFFLINE_VERSION = 1;\nvar CACHE_NAME = \"offline\";\nvar OFFLINE_URL = \"offline.html\";\nself.addEventListener(\"install\", function (event) {\n  console.debug(\"Install service worker\");\n  event.waitUntil((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {\n    var cache;\n    return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee$(_context) {\n      while (1) {\n        switch (_context.prev = _context.next) {\n          case 0:\n            _context.next = 2;\n            return caches.open(CACHE_NAME);\n\n          case 2:\n            cache = _context.sent;\n            _context.next = 5;\n            return cache.add(new Request(OFFLINE_URL, {\n              cache: \"reload\"\n            }));\n\n          case 5:\n            console.debug(\"Service worker installed\");\n\n          case 6:\n          case \"end\":\n            return _context.stop();\n        }\n      }\n    }, _callee);\n  }))());\n  void self.skipWaiting();\n});\nself.addEventListener(\"activate\", function (event) {\n  console.debug(\"Activate service worker\");\n  event.waitUntil((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee2() {\n    return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee2$(_context2) {\n      while (1) {\n        switch (_context2.prev = _context2.next) {\n          case 0:\n            if (!(\"navigationPreload\" in self.registration)) {\n              _context2.next = 4;\n              break;\n            }\n\n            _context2.next = 3;\n            return self.registration.navigationPreload.enable();\n\n          case 3:\n            console.debug(\"Service worker enabled navigationPreload\");\n\n          case 4:\n          case \"end\":\n            return _context2.stop();\n        }\n      }\n    }, _callee2);\n  }))());\n  void self.clients.claim();\n});\nself.addEventListener(\"fetch\", function (event) {\n  if (event.request.mode === \"navigate\") {\n    event.respondWith((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee3() {\n      var preloadResponse, networkResponse, cache, cachedResponse;\n      return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee3$(_context3) {\n        while (1) {\n          switch (_context3.prev = _context3.next) {\n            case 0:\n              _context3.prev = 0;\n              _context3.next = 3;\n              return event.preloadResponse;\n\n            case 3:\n              preloadResponse = _context3.sent;\n\n              if (!preloadResponse) {\n                _context3.next = 7;\n                break;\n              }\n\n              console.debug(\"Returning preloaded response for fetch request\");\n              return _context3.abrupt(\"return\", preloadResponse);\n\n            case 7:\n              _context3.next = 9;\n              return fetch(event.request);\n\n            case 9:\n              networkResponse = _context3.sent;\n              console.debug(\"Returning network response for fetch request\");\n              return _context3.abrupt(\"return\", networkResponse);\n\n            case 14:\n              _context3.prev = 14;\n              _context3.t0 = _context3[\"catch\"](0);\n              // catch is only triggered if an exception is thrown, which is likely\n              // due to a network error.\n              // If fetch() returns a valid HTTP response with a response code in\n              // the 4xx or 5xx range, the catch() will NOT be called.\n              console.debug(\"Fetch failed; returning offline page instead: \".concat(_context3.t0));\n              _context3.next = 19;\n              return caches.open(CACHE_NAME);\n\n            case 19:\n              cache = _context3.sent;\n              _context3.next = 22;\n              return cache.match(OFFLINE_URL);\n\n            case 22:\n              cachedResponse = _context3.sent;\n              return _context3.abrupt(\"return\", cachedResponse);\n\n            case 24:\n            case \"end\":\n              return _context3.stop();\n          }\n        }\n      }, _callee3, null, [[0, 14]]);\n    }))());\n  } else {\n    console.debug(\"Fetch request not handled by service worker\");\n  }\n});\n/* harmony default export */ __webpack_exports__[\"default\"] = (null);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,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QTtBQUFBO0FBQUE7QUFBQSxLQUFELElBREY7QUE2QkQsR0E5QkQsTUE4Qk87QUFDTHJCLElBQUFBLE9BQU8sQ0FBQ0MsS0FBUixDQUFjLDZDQUFkO0FBQ0Q7QUFDRixDQWxDRDtBQW9DQSwrREFBZSxJQUFmIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vc3ByaXRzdGF0Ly4vc3JjL3NlcnZpY2Utd29ya2VyLnRzPzdmNTgiXSwic291cmNlc0NvbnRlbnQiOlsiZGVjbGFyZSBjb25zdCBzZWxmOiBTZXJ2aWNlV29ya2VyR2xvYmFsU2NvcGU7XG5cbi8vIEluY3JlbWVudGluZyBPRkZMSU5FX1ZFUlNJT04gd2lsbCBraWNrIG9mZiB0aGUgaW5zdGFsbCBldmVudCBhbmQgZm9yY2Vcbi8vICBwcmV2aW91c2x5IGNhY2hlZCByZXNvdXJjZXMgdG8gYmUgdXBkYXRlZCBmcm9tIHRoZSBuZXR3b3JrLlxuLy8gVGhpcyB2YXJpYWJsZSBpcyBpbnRlbnRpb25hbGx5IGRlY2xhcmVkIGFuZCB1bnVzZWQuXG5jb25zdCBPRkZMSU5FX1ZFUlNJT04gPSAxO1xuY29uc3QgQ0FDSEVfTkFNRSA9IFwib2ZmbGluZVwiO1xuY29uc3QgT0ZGTElORV9VUkwgPSBcIm9mZmxpbmUuaHRtbFwiO1xuXG5zZWxmLmFkZEV2ZW50TGlzdGVuZXIoXCJpbnN0YWxsXCIsIChldmVudCkgPT4ge1xuICBjb25zb2xlLmRlYnVnKFwiSW5zdGFsbCBzZXJ2aWNlIHdvcmtlclwiKTtcblxuICBldmVudC53YWl0VW50aWwoXG4gICAgKGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGNhY2hlID0gYXdhaXQgY2FjaGVzLm9wZW4oQ0FDSEVfTkFNRSk7XG4gICAgICBhd2FpdCBjYWNoZS5hZGQobmV3IFJlcXVlc3QoT0ZGTElORV9VUkwsIHtjYWNoZTogXCJyZWxvYWRcIn0pKTtcblxuICAgICAgY29uc29sZS5kZWJ1ZyhcIlNlcnZpY2Ugd29ya2VyIGluc3RhbGxlZFwiKTtcbiAgICB9KSgpXG4gICk7XG4gIHZvaWQgc2VsZi5za2lwV2FpdGluZygpO1xufSk7XG5cbnNlbGYuYWRkRXZlbnRMaXN0ZW5lcihcImFjdGl2YXRlXCIsIChldmVudCkgPT4ge1xuICBjb25zb2xlLmRlYnVnKFwiQWN0aXZhdGUgc2VydmljZSB3b3JrZXJcIik7XG5cbiAgZXZlbnQud2FpdFVudGlsKFxuICAgIChhc3luYyAoKSA9PiB7XG4gICAgICAvLyBFbmFibGUgbmF2aWdhdGlvbiBwcmVsb2FkIGlmIGl0J3Mgc3VwcG9ydGVkLlxuICAgICAgLy8gU2VlIGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3dlYi91cGRhdGVzLzIwMTcvMDIvbmF2aWdhdGlvbi1wcmVsb2FkXG4gICAgICBpZiAoXCJuYXZpZ2F0aW9uUHJlbG9hZFwiIGluIHNlbGYucmVnaXN0cmF0aW9uKSB7XG4gICAgICAgIC8vIEB0cy1pZ25vcmUgdGhpcyBpcyBvbmx5IGNhbGxlZCBpZiB0aGUgYnJvd3NlciBzdXBwb3J0cyBpdFxuICAgICAgICBhd2FpdCBzZWxmLnJlZ2lzdHJhdGlvbi5uYXZpZ2F0aW9uUHJlbG9hZC5lbmFibGUoKTtcblxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiU2VydmljZSB3b3JrZXIgZW5hYmxlZCBuYXZpZ2F0aW9uUHJlbG9hZFwiKTtcbiAgICAgIH1cbiAgICB9KSgpXG4gICk7XG5cbiAgdm9pZCBzZWxmLmNsaWVudHMuY2xhaW0oKTtcbn0pO1xuXG5zZWxmLmFkZEV2ZW50TGlzdGVuZXIoXCJmZXRjaFwiLCAoZXZlbnQpID0+IHtcbiAgaWYgKGV2ZW50LnJlcXVlc3QubW9kZSA9PT0gXCJuYXZpZ2F0ZVwiKSB7XG4gICAgZXZlbnQucmVzcG9uZFdpdGgoXG4gICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgICBjb25zdCBwcmVsb2FkUmVzcG9uc2UgPSBhd2FpdCBldmVudC5wcmVsb2FkUmVzcG9uc2U7XG4gICAgICAgICAgaWYgKHByZWxvYWRSZXNwb25zZSkge1xuICAgICAgICAgICAgY29uc29sZS5kZWJ1ZyhcIlJldHVybmluZyBwcmVsb2FkZWQgcmVzcG9uc2UgZm9yIGZldGNoIHJlcXVlc3RcIik7XG5cbiAgICAgICAgICAgIHJldHVybiBwcmVsb2FkUmVzcG9uc2U7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgbmV0d29ya1Jlc3BvbnNlID0gYXdhaXQgZmV0Y2goZXZlbnQucmVxdWVzdCk7XG5cbiAgICAgICAgICBjb25zb2xlLmRlYnVnKFwiUmV0dXJuaW5nIG5ldHdvcmsgcmVzcG9uc2UgZm9yIGZldGNoIHJlcXVlc3RcIik7XG5cbiAgICAgICAgICByZXR1cm4gbmV0d29ya1Jlc3BvbnNlO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIC8vIGNhdGNoIGlzIG9ubHkgdHJpZ2dlcmVkIGlmIGFuIGV4Y2VwdGlvbiBpcyB0aHJvd24sIHdoaWNoIGlzIGxpa2VseVxuICAgICAgICAgIC8vIGR1ZSB0byBhIG5ldHdvcmsgZXJyb3IuXG4gICAgICAgICAgLy8gSWYgZmV0Y2goKSByZXR1cm5zIGEgdmFsaWQgSFRUUCByZXNwb25zZSB3aXRoIGEgcmVzcG9uc2UgY29kZSBpblxuICAgICAgICAgIC8vIHRoZSA0eHggb3IgNXh4IHJhbmdlLCB0aGUgY2F0Y2goKSB3aWxsIE5PVCBiZSBjYWxsZWQuXG4gICAgICAgICAgY29uc29sZS5kZWJ1ZyhgRmV0Y2ggZmFpbGVkOyByZXR1cm5pbmcgb2ZmbGluZSBwYWdlIGluc3RlYWQ6ICR7ZXJyb3J9YCk7XG5cbiAgICAgICAgICBjb25zdCBjYWNoZSA9IGF3YWl0IGNhY2hlcy5vcGVuKENBQ0hFX05BTUUpO1xuICAgICAgICAgIGNvbnN0IGNhY2hlZFJlc3BvbnNlID0gYXdhaXQgY2FjaGUubWF0Y2goT0ZGTElORV9VUkwpO1xuICAgICAgICAgIHJldHVybiBjYWNoZWRSZXNwb25zZTtcbiAgICAgICAgfVxuICAgICAgfSkoKVxuICAgICk7XG4gIH0gZWxzZSB7XG4gICAgY29uc29sZS5kZWJ1ZyhcIkZldGNoIHJlcXVlc3Qgbm90IGhhbmRsZWQgYnkgc2VydmljZSB3b3JrZXJcIik7XG4gIH1cbn0pO1xuXG5leHBvcnQgZGVmYXVsdCBudWxsO1xuIl0sIm5hbWVzIjpbIk9GRkxJTkVfVkVSU0lPTiIsIkNBQ0hFX05BTUUiLCJPRkZMSU5FX1VSTCIsInNlbGYiLCJhZGRFdmVudExpc3RlbmVyIiwiZXZlbnQiLCJjb25zb2xlIiwiZGVidWciLCJ3YWl0VW50aWwiLCJjYWNoZXMiLCJvcGVuIiwiY2FjaGUiLCJhZGQiLCJSZXF1ZXN0Iiwic2tpcFdhaXRpbmciLCJyZWdpc3RyYXRpb24iLCJuYXZpZ2F0aW9uUHJlbG9hZCIsImVuYWJsZSIsImNsaWVudHMiLCJjbGFpbSIsInJlcXVlc3QiLCJtb2RlIiwicmVzcG9uZFdpdGgiLCJwcmVsb2FkUmVzcG9uc2UiLCJmZXRjaCIsIm5ldHdvcmtSZXNwb25zZSIsIm1hdGNoIiwiY2FjaGVkUmVzcG9uc2UiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/service-worker.ts\n");

/***/ })

},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
/******/ __webpack_require__.O(0, ["framework-node_modules_babel_runtime_regenerator_index_js-node_modules_babel_runtime_helpers_-23c3ae"], function() { return __webpack_exec__("./src/service-worker.ts"); });
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ }
]);

index.ts 在其中服务工作者获取注册:

import "./index.sass";
import "intro.js/introjs.css";
import "intro.js/themes/introjs-modern.css"

import React from "react";
import {createRoot} from "react-dom/client";
import {BrowserRouter} from "react-router-dom";
import {Provider} from "react-redux";

import App from "./app/App";
import {store} from "./app/store";


window.addEventListener("load", async () => {
  if ("serviceWorker" in navigator) {
    console.debug("Register service worker");

    await navigator.serviceWorker.register("/service-worker.js");
  }
});

console.debug("Start application");

const container = document.getElementById("root");
const root = createRoot(container!);
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App/>
      </BrowserRouter>
    </Provider>
  </React.StrictMode>
);

package.json

{
  "name": "test",
  "version": "0.1.0",
  "homepage": ".",
  "browser": "webpack.config.js",
  "scripts": {
    "start": "npm run trans:compile && webpack --mode=development --watch",
    "build:dev": "npm run trans:compile && webpack --mode=development",
    "build": "npm run trans:compile && webpack --mode=production",
    "db:flush": "python ../manage.py flush --noinput",
    "db:seed": "python ../manage.py loaddata",
    "test": "cypress run",
    "cy:open": "cypress open",
    "trans:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file translation/locales/de.json",
    "trans:manage": "babel-node -x .ts -- translation/manageTranslations.ts",
    "trans": "npm run trans:extract && npm run trans:manage",
    "trans:compile": "formatjs compile 'translation/locales/en.json' --ast --out-file 'src/locales/en.json'"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version",
      "last 1 opera version"
    ]
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^6.1.1",
    "@fortawesome/free-brands-svg-icons": "^6.1.1",
    "@fortawesome/free-solid-svg-icons": "^6.1.1",
    "@fortawesome/react-fontawesome": "^0.1.18",
    "@reduxjs/toolkit": "^1.8.1",
    "bulma": "^0.9.3",
    "bulma-switch": "^2.0.4",
    "chart.js": "^3.7.1",
    "chartjs-plugin-zoom": "^1.2.1",
    "intro.js": "^5.1.0",
    "intro.js-react": "^0.6.0",
    "lodash.debounce": "^4.0.8",
    "moment-timezone": "^0.5.34",
    "nanoid": "^3.3.3",
    "react": "^18.1.0",
    "react-cookie": "^4.1.1",
    "react-dom": "^18.1.0",
    "react-intl": "^5.25.1",
    "react-redux": "^8.0.1",
    "react-router-dom": "^6.3.0",
    "universal-cookie": "^4.0.4"
  },
  "devDependencies": {
    "@babel/core": "^7.17.9",
    "@babel/node": "^7.16.8",
    "@babel/plugin-transform-runtime": "^7.17.0",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "@babel/preset-typescript": "^7.16.7",
    "@babel/runtime": "^7.17.9",
    "@formatjs/cli": "^4.8.4",
    "@types/google.maps": "^3.48.7",
    "@types/intro.js": "^3.0.2",
    "@types/lodash.debounce": "^4.0.7",
    "@types/node": "^17.0.30",
    "@types/react": "^18.0.8",
    "@types/react-dom": "^18.0.3",
    "@types/react-router-dom": "^5.3.3",
    "babel-loader": "^8.2.5",
    "babel-plugin-formatjs": "^10.3.20",
    "clean-webpack-plugin": "^4.0.0",
    "compression-webpack-plugin": "^9.2.0",
    "copy-webpack-plugin": "^10.2.4",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^3.4.1",
    "cypress": "^9.6.0",
    "cypress-real-events": "^1.7.0",
    "file-loader": "^6.2.0",
    "mini-css-extract-plugin": "^2.6.0",
    "moment-locales-webpack-plugin": "^1.2.0",
    "node-sass": "^7.0.1",
    "prettier": "^2.6.2",
    "sass-loader": "^12.6.0",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.1",
    "typescript": "^4.6.4",
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2",
    "webpack-manifest-plugin": "^5.0.0"
  },
  "keywords": [],
  "author": "eega",
  "license": "MIT",
  "description": ""
}

babel.config.json

{
  "plugins": [
    "@babel/plugin-transform-runtime",
    [
      "formatjs", {
        "idInterpolationPattern": "[sha512:contenthash:base64:6]",
        "ast": true
      }
    ]
  ],
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "module": "ESNext",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es2017.intl",
      "es2018.intl",
      "webworker"
    ],
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src/**/*",
  ]
}

webpack.config.json

const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const {resolve} = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const zlib = require("zlib");


module.exports = {
  devtool: "eval-source-map",
  entry: {
    app: resolve(__dirname, "src/index.tsx"),
    "service-worker": {
      import: resolve(__dirname, "src/service-worker.ts"),
      filename: "js/service-worker.js"
    }
  },
  mode: process.env.NODE_ENV ? process.env.NODE_ENV : "production",
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,

        exclude: /node_modules\/(?!react-intl|intl-messageformat|@formatjs\/icu-messageformat-parser)/,
        use: ["babel-loader"],
      },
      {
        test: /.*/,
        include: resolve(__dirname, "assets/img"),
        options: {
          context: resolve(__dirname, "assets/"),
          name: "[path][name]-[contenthash].[ext]",
        },
        loader: "file-loader",
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.sass$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  output: {
    path: resolve(__dirname, "public/"),
    filename: "js/[name]-[contenthash].js",
    chunkFilename: "js/[name]-[contenthash].chunk.js",
  },
  optimization: {
    runtimeChunk: {
      name: "runtime"
    },
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        // disable webpack's default cacheGroup
        default: false,
        // disable webpack's default vendor cacheGroup
        vendors: false,
        // Create a framework bundle that contains React libraries
        // They hardly change so we bundle them together to improve
        framework: {},
        // Big modules that are over 160kb are moved to their own file to
        // optimize browser parsing & execution
        lib: {},
        // All libraries that are used on all pages are moved into a common chunk
        commons: {},
        // When a module is used more than once we create a shared bundle to save user's bandwidth
        shared: {},
        // All CSS is bundled into one stylesheet
        styles: {}
      },
      // Keep maximum initial requests to 25
      maxInitialRequests: 25,
      // A chunk should be at least 20kb before using splitChunks
      minSize: 20000
    },
    minimizer: [
      new CompressionPlugin({
        filename: "[path][base].gz",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CompressionPlugin({
        filename: "[path][base].br",
        algorithm: "brotliCompress",
        test: /\.(js|css|html|png|svg)$/,
        compressionOptions: {
          params: {
            [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
          },
        },
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CssMinimizerPlugin(),
      new TerserPlugin(),
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new CleanWebpackPlugin(),
    new WebpackManifestPlugin({
      fileName: "webpack_manifest.json",
      publicPath: ""
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      chunkFilename: "css/[id]-[contenthash].css",
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/favicon.ico",
          to: "img",
        },
      ],
    }),
    // These are files used in the progressive web app manifest only, so they wouldn't
    //  be processed without this.
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/logo_small_*.png",
          to: "img/[name]-[contenthash].png",
          toType: "template"
        },
      ],
    }),
    new MomentLocalesPlugin({
      localesToKeep: ["de", "en"],
    })
  ],
  resolve: {
    extensions: [".js", ".jsx", ".tsx", ".ts"],
    modules: [
      resolve(__dirname, "src"),
      resolve(__dirname, "node_modules"),
    ]
  },
};

I'm trying to implement a service worker, but fail to make it execute the service worker code work if it is transpiled for Typescript with Webpack + Bable. Registration works fine and the worker is shown as activated, but neither the install, nor activate events are ever fired - I assume due to the code not being executed correctly. Now, if I replace the transpiled code with the same code in plain Javascript everything works fine.

Everything else is working fine in the browser, so it doesn't seem to be some inherent issue with my settings - at least none I understand. I'd really like to understand what is causing this issue!

Here is the relevant code and config:

service-worker.ts:

declare const self: ServiceWorkerGlobalScope;

// Incrementing OFFLINE_VERSION will kick off the install event and force
//  previously cached resources to be updated from the network.
// This variable is intentionally declared and unused.
const OFFLINE_VERSION = 1;
const CACHE_NAME = "offline";
const OFFLINE_URL = "offline.html";

self.addEventListener("install", (event) => {
  console.debug("Install service worker");

  event.waitUntil(
    (async () => {
      const cache = await caches.open(CACHE_NAME);
      await cache.add(new Request(OFFLINE_URL, {cache: "reload"}));

      console.debug("Service worker installed");
    })()
  );
  void self.skipWaiting();
});

self.addEventListener("activate", (event) => {
  console.debug("Activate service worker");

  event.waitUntil(
    (async () => {
      // Enable navigation preload if it's supported.
      // See https://developers.google.com/web/updates/2017/02/navigation-preload
      if ("navigationPreload" in self.registration) {
        // @ts-ignore this is only called if the browser supports it
        await self.registration.navigationPreload.enable();

        console.debug("Service worker enabled navigationPreload");
      }
    })()
  );

  void self.clients.claim();
});

self.addEventListener("fetch", (event) => {
  if (event.request.mode === "navigate") {
    event.respondWith(
      (async () => {
        try {
          // @ts-ignore
          const preloadResponse = await event.preloadResponse;
          if (preloadResponse) {
            console.debug("Returning preloaded response for fetch request");

            return preloadResponse;
          }

          const networkResponse = await fetch(event.request);

          console.debug("Returning network response for fetch request");

          return networkResponse;
        } catch (error) {
          // catch is only triggered if an exception is thrown, which is likely
          // due to a network error.
          // If fetch() returns a valid HTTP response with a response code in
          // the 4xx or 5xx range, the catch() will NOT be called.
          console.debug(`Fetch failed; returning offline page instead: ${error}`);

          const cache = await caches.open(CACHE_NAME);
          const cachedResponse = await cache.match(OFFLINE_URL);
          return cachedResponse;
        }
      })()
    );
  } else {
    console.debug("Fetch request not handled by service worker");
  }
});

export default null;

The transpiled service-worker.js:

"use strict";
/*
 * ATTENTION: An "eval-source-map" devtool has been used.
 * This devtool is neither made for production nor for readable output files.
 * It uses "eval()" calls to create a separate source file with attached SourceMaps in the browser devtools.
 * If you are trying to read the output file, select a different devtool (https://webpack.js.org/configuration/devtool/)
 * or disable the default devtool with "devtool: false".
 * If you are looking for production-ready output files, see mode: "production" (https://webpack.js.org/configuration/mode/).
 */
(self["webpackChunkspritstat"] = self["webpackChunkspritstat"] || []).push([["service-worker"],{

/***/ "./src/service-worker.ts":
/*!*******************************!*\
  !*** ./src/service-worker.ts ***!
  \*******************************/
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {

eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! @babel/runtime/helpers/asyncToGenerator */ \"./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js\");\n/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! @babel/runtime/regenerator */ \"./node_modules/@babel/runtime/regenerator/index.js\");\n/* harmony import */ var _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__);\n\n\n// Incrementing OFFLINE_VERSION will kick off the install event and force\n//  previously cached resources to be updated from the network.\n// This variable is intentionally declared and unused.\nvar OFFLINE_VERSION = 1;\nvar CACHE_NAME = \"offline\";\nvar OFFLINE_URL = \"offline.html\";\nself.addEventListener(\"install\", function (event) {\n  console.debug(\"Install service worker\");\n  event.waitUntil((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee() {\n    var cache;\n    return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee$(_context) {\n      while (1) {\n        switch (_context.prev = _context.next) {\n          case 0:\n            _context.next = 2;\n            return caches.open(CACHE_NAME);\n\n          case 2:\n            cache = _context.sent;\n            _context.next = 5;\n            return cache.add(new Request(OFFLINE_URL, {\n              cache: \"reload\"\n            }));\n\n          case 5:\n            console.debug(\"Service worker installed\");\n\n          case 6:\n          case \"end\":\n            return _context.stop();\n        }\n      }\n    }, _callee);\n  }))());\n  void self.skipWaiting();\n});\nself.addEventListener(\"activate\", function (event) {\n  console.debug(\"Activate service worker\");\n  event.waitUntil((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee2() {\n    return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee2$(_context2) {\n      while (1) {\n        switch (_context2.prev = _context2.next) {\n          case 0:\n            if (!(\"navigationPreload\" in self.registration)) {\n              _context2.next = 4;\n              break;\n            }\n\n            _context2.next = 3;\n            return self.registration.navigationPreload.enable();\n\n          case 3:\n            console.debug(\"Service worker enabled navigationPreload\");\n\n          case 4:\n          case \"end\":\n            return _context2.stop();\n        }\n      }\n    }, _callee2);\n  }))());\n  void self.clients.claim();\n});\nself.addEventListener(\"fetch\", function (event) {\n  if (event.request.mode === \"navigate\") {\n    event.respondWith((0,_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__[\"default\"])( /*#__PURE__*/_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark(function _callee3() {\n      var preloadResponse, networkResponse, cache, cachedResponse;\n      return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap(function _callee3$(_context3) {\n        while (1) {\n          switch (_context3.prev = _context3.next) {\n            case 0:\n              _context3.prev = 0;\n              _context3.next = 3;\n              return event.preloadResponse;\n\n            case 3:\n              preloadResponse = _context3.sent;\n\n              if (!preloadResponse) {\n                _context3.next = 7;\n                break;\n              }\n\n              console.debug(\"Returning preloaded response for fetch request\");\n              return _context3.abrupt(\"return\", preloadResponse);\n\n            case 7:\n              _context3.next = 9;\n              return fetch(event.request);\n\n            case 9:\n              networkResponse = _context3.sent;\n              console.debug(\"Returning network response for fetch request\");\n              return _context3.abrupt(\"return\", networkResponse);\n\n            case 14:\n              _context3.prev = 14;\n              _context3.t0 = _context3[\"catch\"](0);\n              // catch is only triggered if an exception is thrown, which is likely\n              // due to a network error.\n              // If fetch() returns a valid HTTP response with a response code in\n              // the 4xx or 5xx range, the catch() will NOT be called.\n              console.debug(\"Fetch failed; returning offline page instead: \".concat(_context3.t0));\n              _context3.next = 19;\n              return caches.open(CACHE_NAME);\n\n            case 19:\n              cache = _context3.sent;\n              _context3.next = 22;\n              return cache.match(OFFLINE_URL);\n\n            case 22:\n              cachedResponse = _context3.sent;\n              return _context3.abrupt(\"return\", cachedResponse);\n\n            case 24:\n            case \"end\":\n              return _context3.stop();\n          }\n        }\n      }, _callee3, null, [[0, 14]]);\n    }))());\n  } else {\n    console.debug(\"Fetch request not handled by service worker\");\n  }\n});\n/* harmony default export */ __webpack_exports__[\"default\"] = (null);//# sourceURL=[module]\n//# sourceMappingURL=data:application/json;charset=utf-8;base64,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QTtBQUFBO0FBQUE7QUFBQSxLQUFELElBREY7QUE2QkQsR0E5QkQsTUE4Qk87QUFDTHJCLElBQUFBLE9BQU8sQ0FBQ0MsS0FBUixDQUFjLDZDQUFkO0FBQ0Q7QUFDRixDQWxDRDtBQW9DQSwrREFBZSxJQUFmIiwic291cmNlcyI6WyJ3ZWJwYWNrOi8vc3ByaXRzdGF0Ly4vc3JjL3NlcnZpY2Utd29ya2VyLnRzPzdmNTgiXSwic291cmNlc0NvbnRlbnQiOlsiZGVjbGFyZSBjb25zdCBzZWxmOiBTZXJ2aWNlV29ya2VyR2xvYmFsU2NvcGU7XG5cbi8vIEluY3JlbWVudGluZyBPRkZMSU5FX1ZFUlNJT04gd2lsbCBraWNrIG9mZiB0aGUgaW5zdGFsbCBldmVudCBhbmQgZm9yY2Vcbi8vICBwcmV2aW91c2x5IGNhY2hlZCByZXNvdXJjZXMgdG8gYmUgdXBkYXRlZCBmcm9tIHRoZSBuZXR3b3JrLlxuLy8gVGhpcyB2YXJpYWJsZSBpcyBpbnRlbnRpb25hbGx5IGRlY2xhcmVkIGFuZCB1bnVzZWQuXG5jb25zdCBPRkZMSU5FX1ZFUlNJT04gPSAxO1xuY29uc3QgQ0FDSEVfTkFNRSA9IFwib2ZmbGluZVwiO1xuY29uc3QgT0ZGTElORV9VUkwgPSBcIm9mZmxpbmUuaHRtbFwiO1xuXG5zZWxmLmFkZEV2ZW50TGlzdGVuZXIoXCJpbnN0YWxsXCIsIChldmVudCkgPT4ge1xuICBjb25zb2xlLmRlYnVnKFwiSW5zdGFsbCBzZXJ2aWNlIHdvcmtlclwiKTtcblxuICBldmVudC53YWl0VW50aWwoXG4gICAgKGFzeW5jICgpID0+IHtcbiAgICAgIGNvbnN0IGNhY2hlID0gYXdhaXQgY2FjaGVzLm9wZW4oQ0FDSEVfTkFNRSk7XG4gICAgICBhd2FpdCBjYWNoZS5hZGQobmV3IFJlcXVlc3QoT0ZGTElORV9VUkwsIHtjYWNoZTogXCJyZWxvYWRcIn0pKTtcblxuICAgICAgY29uc29sZS5kZWJ1ZyhcIlNlcnZpY2Ugd29ya2VyIGluc3RhbGxlZFwiKTtcbiAgICB9KSgpXG4gICk7XG4gIHZvaWQgc2VsZi5za2lwV2FpdGluZygpO1xufSk7XG5cbnNlbGYuYWRkRXZlbnRMaXN0ZW5lcihcImFjdGl2YXRlXCIsIChldmVudCkgPT4ge1xuICBjb25zb2xlLmRlYnVnKFwiQWN0aXZhdGUgc2VydmljZSB3b3JrZXJcIik7XG5cbiAgZXZlbnQud2FpdFVudGlsKFxuICAgIChhc3luYyAoKSA9PiB7XG4gICAgICAvLyBFbmFibGUgbmF2aWdhdGlvbiBwcmVsb2FkIGlmIGl0J3Mgc3VwcG9ydGVkLlxuICAgICAgLy8gU2VlIGh0dHBzOi8vZGV2ZWxvcGVycy5nb29nbGUuY29tL3dlYi91cGRhdGVzLzIwMTcvMDIvbmF2aWdhdGlvbi1wcmVsb2FkXG4gICAgICBpZiAoXCJuYXZpZ2F0aW9uUHJlbG9hZFwiIGluIHNlbGYucmVnaXN0cmF0aW9uKSB7XG4gICAgICAgIC8vIEB0cy1pZ25vcmUgdGhpcyBpcyBvbmx5IGNhbGxlZCBpZiB0aGUgYnJvd3NlciBzdXBwb3J0cyBpdFxuICAgICAgICBhd2FpdCBzZWxmLnJlZ2lzdHJhdGlvbi5uYXZpZ2F0aW9uUHJlbG9hZC5lbmFibGUoKTtcblxuICAgICAgICBjb25zb2xlLmRlYnVnKFwiU2VydmljZSB3b3JrZXIgZW5hYmxlZCBuYXZpZ2F0aW9uUHJlbG9hZFwiKTtcbiAgICAgIH1cbiAgICB9KSgpXG4gICk7XG5cbiAgdm9pZCBzZWxmLmNsaWVudHMuY2xhaW0oKTtcbn0pO1xuXG5zZWxmLmFkZEV2ZW50TGlzdGVuZXIoXCJmZXRjaFwiLCAoZXZlbnQpID0+IHtcbiAgaWYgKGV2ZW50LnJlcXVlc3QubW9kZSA9PT0gXCJuYXZpZ2F0ZVwiKSB7XG4gICAgZXZlbnQucmVzcG9uZFdpdGgoXG4gICAgICAoYXN5bmMgKCkgPT4ge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIC8vIEB0cy1pZ25vcmVcbiAgICAgICAgICBjb25zdCBwcmVsb2FkUmVzcG9uc2UgPSBhd2FpdCBldmVudC5wcmVsb2FkUmVzcG9uc2U7XG4gICAgICAgICAgaWYgKHByZWxvYWRSZXNwb25zZSkge1xuICAgICAgICAgICAgY29uc29sZS5kZWJ1ZyhcIlJldHVybmluZyBwcmVsb2FkZWQgcmVzcG9uc2UgZm9yIGZldGNoIHJlcXVlc3RcIik7XG5cbiAgICAgICAgICAgIHJldHVybiBwcmVsb2FkUmVzcG9uc2U7XG4gICAgICAgICAgfVxuXG4gICAgICAgICAgY29uc3QgbmV0d29ya1Jlc3BvbnNlID0gYXdhaXQgZmV0Y2goZXZlbnQucmVxdWVzdCk7XG5cbiAgICAgICAgICBjb25zb2xlLmRlYnVnKFwiUmV0dXJuaW5nIG5ldHdvcmsgcmVzcG9uc2UgZm9yIGZldGNoIHJlcXVlc3RcIik7XG5cbiAgICAgICAgICByZXR1cm4gbmV0d29ya1Jlc3BvbnNlO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIC8vIGNhdGNoIGlzIG9ubHkgdHJpZ2dlcmVkIGlmIGFuIGV4Y2VwdGlvbiBpcyB0aHJvd24sIHdoaWNoIGlzIGxpa2VseVxuICAgICAgICAgIC8vIGR1ZSB0byBhIG5ldHdvcmsgZXJyb3IuXG4gICAgICAgICAgLy8gSWYgZmV0Y2goKSByZXR1cm5zIGEgdmFsaWQgSFRUUCByZXNwb25zZSB3aXRoIGEgcmVzcG9uc2UgY29kZSBpblxuICAgICAgICAgIC8vIHRoZSA0eHggb3IgNXh4IHJhbmdlLCB0aGUgY2F0Y2goKSB3aWxsIE5PVCBiZSBjYWxsZWQuXG4gICAgICAgICAgY29uc29sZS5kZWJ1ZyhgRmV0Y2ggZmFpbGVkOyByZXR1cm5pbmcgb2ZmbGluZSBwYWdlIGluc3RlYWQ6ICR7ZXJyb3J9YCk7XG5cbiAgICAgICAgICBjb25zdCBjYWNoZSA9IGF3YWl0IGNhY2hlcy5vcGVuKENBQ0hFX05BTUUpO1xuICAgICAgICAgIGNvbnN0IGNhY2hlZFJlc3BvbnNlID0gYXdhaXQgY2FjaGUubWF0Y2goT0ZGTElORV9VUkwpO1xuICAgICAgICAgIHJldHVybiBjYWNoZWRSZXNwb25zZTtcbiAgICAgICAgfVxuICAgICAgfSkoKVxuICAgICk7XG4gIH0gZWxzZSB7XG4gICAgY29uc29sZS5kZWJ1ZyhcIkZldGNoIHJlcXVlc3Qgbm90IGhhbmRsZWQgYnkgc2VydmljZSB3b3JrZXJcIik7XG4gIH1cbn0pO1xuXG5leHBvcnQgZGVmYXVsdCBudWxsO1xuIl0sIm5hbWVzIjpbIk9GRkxJTkVfVkVSU0lPTiIsIkNBQ0hFX05BTUUiLCJPRkZMSU5FX1VSTCIsInNlbGYiLCJhZGRFdmVudExpc3RlbmVyIiwiZXZlbnQiLCJjb25zb2xlIiwiZGVidWciLCJ3YWl0VW50aWwiLCJjYWNoZXMiLCJvcGVuIiwiY2FjaGUiLCJhZGQiLCJSZXF1ZXN0Iiwic2tpcFdhaXRpbmciLCJyZWdpc3RyYXRpb24iLCJuYXZpZ2F0aW9uUHJlbG9hZCIsImVuYWJsZSIsImNsaWVudHMiLCJjbGFpbSIsInJlcXVlc3QiLCJtb2RlIiwicmVzcG9uZFdpdGgiLCJwcmVsb2FkUmVzcG9uc2UiLCJmZXRjaCIsIm5ldHdvcmtSZXNwb25zZSIsIm1hdGNoIiwiY2FjaGVkUmVzcG9uc2UiXSwic291cmNlUm9vdCI6IiJ9\n//# sourceURL=webpack-internal:///./src/service-worker.ts\n");

/***/ })

},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
/******/ __webpack_require__.O(0, ["framework-node_modules_babel_runtime_regenerator_index_js-node_modules_babel_runtime_helpers_-23c3ae"], function() { return __webpack_exec__("./src/service-worker.ts"); });
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ }
]);

index.ts where the service worker gets registered:

import "./index.sass";
import "intro.js/introjs.css";
import "intro.js/themes/introjs-modern.css"

import React from "react";
import {createRoot} from "react-dom/client";
import {BrowserRouter} from "react-router-dom";
import {Provider} from "react-redux";

import App from "./app/App";
import {store} from "./app/store";


window.addEventListener("load", async () => {
  if ("serviceWorker" in navigator) {
    console.debug("Register service worker");

    await navigator.serviceWorker.register("/service-worker.js");
  }
});

console.debug("Start application");

const container = document.getElementById("root");
const root = createRoot(container!);
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <BrowserRouter>
        <App/>
      </BrowserRouter>
    </Provider>
  </React.StrictMode>
);

package.json:

{
  "name": "test",
  "version": "0.1.0",
  "homepage": ".",
  "browser": "webpack.config.js",
  "scripts": {
    "start": "npm run trans:compile && webpack --mode=development --watch",
    "build:dev": "npm run trans:compile && webpack --mode=development",
    "build": "npm run trans:compile && webpack --mode=production",
    "db:flush": "python ../manage.py flush --noinput",
    "db:seed": "python ../manage.py loaddata",
    "test": "cypress run",
    "cy:open": "cypress open",
    "trans:extract": "formatjs extract 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file translation/locales/de.json",
    "trans:manage": "babel-node -x .ts -- translation/manageTranslations.ts",
    "trans": "npm run trans:extract && npm run trans:manage",
    "trans:compile": "formatjs compile 'translation/locales/en.json' --ast --out-file 'src/locales/en.json'"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version",
      "last 1 opera version"
    ]
  },
  "dependencies": {
    "@fortawesome/fontawesome-svg-core": "^6.1.1",
    "@fortawesome/free-brands-svg-icons": "^6.1.1",
    "@fortawesome/free-solid-svg-icons": "^6.1.1",
    "@fortawesome/react-fontawesome": "^0.1.18",
    "@reduxjs/toolkit": "^1.8.1",
    "bulma": "^0.9.3",
    "bulma-switch": "^2.0.4",
    "chart.js": "^3.7.1",
    "chartjs-plugin-zoom": "^1.2.1",
    "intro.js": "^5.1.0",
    "intro.js-react": "^0.6.0",
    "lodash.debounce": "^4.0.8",
    "moment-timezone": "^0.5.34",
    "nanoid": "^3.3.3",
    "react": "^18.1.0",
    "react-cookie": "^4.1.1",
    "react-dom": "^18.1.0",
    "react-intl": "^5.25.1",
    "react-redux": "^8.0.1",
    "react-router-dom": "^6.3.0",
    "universal-cookie": "^4.0.4"
  },
  "devDependencies": {
    "@babel/core": "^7.17.9",
    "@babel/node": "^7.16.8",
    "@babel/plugin-transform-runtime": "^7.17.0",
    "@babel/preset-env": "^7.16.11",
    "@babel/preset-react": "^7.16.7",
    "@babel/preset-typescript": "^7.16.7",
    "@babel/runtime": "^7.17.9",
    "@formatjs/cli": "^4.8.4",
    "@types/google.maps": "^3.48.7",
    "@types/intro.js": "^3.0.2",
    "@types/lodash.debounce": "^4.0.7",
    "@types/node": "^17.0.30",
    "@types/react": "^18.0.8",
    "@types/react-dom": "^18.0.3",
    "@types/react-router-dom": "^5.3.3",
    "babel-loader": "^8.2.5",
    "babel-plugin-formatjs": "^10.3.20",
    "clean-webpack-plugin": "^4.0.0",
    "compression-webpack-plugin": "^9.2.0",
    "copy-webpack-plugin": "^10.2.4",
    "css-loader": "^6.7.1",
    "css-minimizer-webpack-plugin": "^3.4.1",
    "cypress": "^9.6.0",
    "cypress-real-events": "^1.7.0",
    "file-loader": "^6.2.0",
    "mini-css-extract-plugin": "^2.6.0",
    "moment-locales-webpack-plugin": "^1.2.0",
    "node-sass": "^7.0.1",
    "prettier": "^2.6.2",
    "sass-loader": "^12.6.0",
    "style-loader": "^3.3.1",
    "terser-webpack-plugin": "^5.3.1",
    "typescript": "^4.6.4",
    "webpack": "^5.72.0",
    "webpack-cli": "^4.9.2",
    "webpack-manifest-plugin": "^5.0.0"
  },
  "keywords": [],
  "author": "eega",
  "license": "MIT",
  "description": ""
}

babel.config.json:

{
  "plugins": [
    "@babel/plugin-transform-runtime",
    [
      "formatjs", {
        "idInterpolationPattern": "[sha512:contenthash:base64:6]",
        "ast": true
      }
    ]
  ],
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react",
    "@babel/preset-typescript"
  ]
}

tsconfig.json:

{
  "compilerOptions": {
    "target": "es5",
    "module": "ESNext",
    "lib": [
      "dom",
      "dom.iterable",
      "esnext",
      "es2017.intl",
      "es2018.intl",
      "webworker"
    ],
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx"
  },
  "include": [
    "src/**/*",
  ]
}

webpack.config.json:

const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const {resolve} = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const zlib = require("zlib");


module.exports = {
  devtool: "eval-source-map",
  entry: {
    app: resolve(__dirname, "src/index.tsx"),
    "service-worker": {
      import: resolve(__dirname, "src/service-worker.ts"),
      filename: "js/service-worker.js"
    }
  },
  mode: process.env.NODE_ENV ? process.env.NODE_ENV : "production",
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,

        exclude: /node_modules\/(?!react-intl|intl-messageformat|@formatjs\/icu-messageformat-parser)/,
        use: ["babel-loader"],
      },
      {
        test: /.*/,
        include: resolve(__dirname, "assets/img"),
        options: {
          context: resolve(__dirname, "assets/"),
          name: "[path][name]-[contenthash].[ext]",
        },
        loader: "file-loader",
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.sass$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  output: {
    path: resolve(__dirname, "public/"),
    filename: "js/[name]-[contenthash].js",
    chunkFilename: "js/[name]-[contenthash].chunk.js",
  },
  optimization: {
    runtimeChunk: {
      name: "runtime"
    },
    splitChunks: {
      chunks: "all",
      cacheGroups: {
        // disable webpack's default cacheGroup
        default: false,
        // disable webpack's default vendor cacheGroup
        vendors: false,
        // Create a framework bundle that contains React libraries
        // They hardly change so we bundle them together to improve
        framework: {},
        // Big modules that are over 160kb are moved to their own file to
        // optimize browser parsing & execution
        lib: {},
        // All libraries that are used on all pages are moved into a common chunk
        commons: {},
        // When a module is used more than once we create a shared bundle to save user's bandwidth
        shared: {},
        // All CSS is bundled into one stylesheet
        styles: {}
      },
      // Keep maximum initial requests to 25
      maxInitialRequests: 25,
      // A chunk should be at least 20kb before using splitChunks
      minSize: 20000
    },
    minimizer: [
      new CompressionPlugin({
        filename: "[path][base].gz",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CompressionPlugin({
        filename: "[path][base].br",
        algorithm: "brotliCompress",
        test: /\.(js|css|html|png|svg)$/,
        compressionOptions: {
          params: {
            [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
          },
        },
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CssMinimizerPlugin(),
      new TerserPlugin(),
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new CleanWebpackPlugin(),
    new WebpackManifestPlugin({
      fileName: "webpack_manifest.json",
      publicPath: ""
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      chunkFilename: "css/[id]-[contenthash].css",
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/favicon.ico",
          to: "img",
        },
      ],
    }),
    // These are files used in the progressive web app manifest only, so they wouldn't
    //  be processed without this.
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/logo_small_*.png",
          to: "img/[name]-[contenthash].png",
          toType: "template"
        },
      ],
    }),
    new MomentLocalesPlugin({
      localesToKeep: ["de", "en"],
    })
  ],
  resolve: {
    extensions: [".js", ".jsx", ".tsx", ".ts"],
    modules: [
      resolve(__dirname, "src"),
      resolve(__dirname, "node_modules"),
    ]
  },
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

带上头具痛哭 2025-02-02 04:06:36

我问题的原因是WebPack优化,即以下2个问题:

  1. 通过设置 runtimechunk config to name:“ runtime” 。这导致将服务工作者提取到一个单独的块中,但
  2. 也可以通过设置

更改 webpack.config.js 这样解决了问题:

const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const {resolve} = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const zlib = require("zlib");


module.exports = {
  devtool: "eval-source-map",
  entry: {
    app: resolve(__dirname, "src/index.tsx"),
    "service-worker": {
      import: resolve(__dirname, "src/service-worker.ts"),
      filename: "js/service-worker.js"
    }
  },
  mode: process.env.NODE_ENV ? process.env.NODE_ENV : "production",
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,

        exclude: /node_modules\/(?!react-intl|intl-messageformat|@formatjs\/icu-messageformat-parser)/,
        use: ["babel-loader"],
      },
      {
        test: /.*/,
        include: resolve(__dirname, "assets/img"),
        options: {
          context: resolve(__dirname, "assets/"),
          name: "[path][name]-[contenthash].[ext]",
        },
        loader: "file-loader",
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.sass$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  output: {
    path: resolve(__dirname, "public/"),
    filename: "js/[name]-[contenthash].js",
    chunkFilename: "js/[name]-[contenthash].chunk.js",
  },
  optimization: {
    runtimeChunk: {
      name: (entrypoint) => {
        if (entrypoint.name.startsWith("service-worker")) {
          return null;
        }

        return `runtime-${entrypoint.name}`
      }
    },
    splitChunks: {
      chunks(chunk) {
        return chunk.name !== "service-worker";
      },
      cacheGroups: {
        // disable webpack's default cacheGroup
        default: false,
        // disable webpack's default vendor cacheGroup
        vendors: false,
        // Create a framework bundle that contains React libraries
        // They hardly change so we bundle them together to improve
        framework: {},
        // Big modules that are over 160kb are moved to their own file to
        // optimize browser parsing & execution
        lib: {},
        // All libraries that are used on all pages are moved into a common chunk
        commons: {},
        // When a module is used more than once we create a shared bundle to save user's bandwidth
        shared: {},
        // All CSS is bundled into one stylesheet
        styles: {}
      },
      // Keep maximum initial requests to 25
      maxInitialRequests: 25,
      // A chunk should be at least 20kb before using splitChunks
      minSize: 20000
    },
    minimizer: [
      new CompressionPlugin({
        filename: "[path][base].gz",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CompressionPlugin({
        filename: "[path][base].br",
        algorithm: "brotliCompress",
        test: /\.(js|css|html|png|svg)$/,
        compressionOptions: {
          params: {
            [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
          },
        },
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CssMinimizerPlugin(),
      new TerserPlugin(),
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new CleanWebpackPlugin(),
    new WebpackManifestPlugin({
      fileName: "webpack_manifest.json",
      publicPath: ""
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      chunkFilename: "css/[id]-[contenthash].css",
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/favicon.ico",
          to: "img",
        },
      ],
    }),
    // These are files used in the progressive web app manifest only, so they wouldn't
    //  be processed without this.
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/logo_small_*.png",
          to: "img/[name]-[contenthash].png",
          toType: "template"
        },
      ],
    }),
    new MomentLocalesPlugin({
      localesToKeep: ["de", "en"],
    })
  ],
  resolve: {
    extensions: [".js", ".jsx", ".tsx", ".ts"],
    modules: [
      resolve(__dirname, "src"),
      resolve(__dirname, "node_modules"),
    ]
  },
};

The cause of my issue was Webpack optimization, namely the following 2 issues:

  1. Extraction of the runtime was enabled for all chunks by setting the runtimeChunk config to name: "runtime". This caused the runtime for the service worker to be extracted into a separate chunk, but this chunk wasn't imported by the service worker
  2. Splitting of chunks was enabled for the service worker also by setting the splitChunks config option chunks to "all", which means that the service worker file was also part of chunking, which led to an transpile error

Changing webpack.config.js like this solved the issue:

const {CleanWebpackPlugin} = require("clean-webpack-plugin");
const CompressionPlugin = require("compression-webpack-plugin");
const CopyWebpackPlugin = require("copy-webpack-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const MomentLocalesPlugin = require("moment-locales-webpack-plugin");
const {resolve} = require("path");
const TerserPlugin = require("terser-webpack-plugin");
const webpack = require("webpack");
const {WebpackManifestPlugin} = require("webpack-manifest-plugin");
const zlib = require("zlib");


module.exports = {
  devtool: "eval-source-map",
  entry: {
    app: resolve(__dirname, "src/index.tsx"),
    "service-worker": {
      import: resolve(__dirname, "src/service-worker.ts"),
      filename: "js/service-worker.js"
    }
  },
  mode: process.env.NODE_ENV ? process.env.NODE_ENV : "production",
  module: {
    rules: [
      {
        test: /\.(ts|js)x?$/,

        exclude: /node_modules\/(?!react-intl|intl-messageformat|@formatjs\/icu-messageformat-parser)/,
        use: ["babel-loader"],
      },
      {
        test: /.*/,
        include: resolve(__dirname, "assets/img"),
        options: {
          context: resolve(__dirname, "assets/"),
          name: "[path][name]-[contenthash].[ext]",
        },
        loader: "file-loader",
      },
      {
        test: /\.css$/i,
        use: ["style-loader", "css-loader"],
      },
      {
        test: /\.sass$/,
        use: [
          MiniCssExtractPlugin.loader,
          "css-loader",
          {
            loader: "sass-loader",
            options: {
              sourceMap: true,
            },
          },
        ],
      },
    ],
  },
  output: {
    path: resolve(__dirname, "public/"),
    filename: "js/[name]-[contenthash].js",
    chunkFilename: "js/[name]-[contenthash].chunk.js",
  },
  optimization: {
    runtimeChunk: {
      name: (entrypoint) => {
        if (entrypoint.name.startsWith("service-worker")) {
          return null;
        }

        return `runtime-${entrypoint.name}`
      }
    },
    splitChunks: {
      chunks(chunk) {
        return chunk.name !== "service-worker";
      },
      cacheGroups: {
        // disable webpack's default cacheGroup
        default: false,
        // disable webpack's default vendor cacheGroup
        vendors: false,
        // Create a framework bundle that contains React libraries
        // They hardly change so we bundle them together to improve
        framework: {},
        // Big modules that are over 160kb are moved to their own file to
        // optimize browser parsing & execution
        lib: {},
        // All libraries that are used on all pages are moved into a common chunk
        commons: {},
        // When a module is used more than once we create a shared bundle to save user's bandwidth
        shared: {},
        // All CSS is bundled into one stylesheet
        styles: {}
      },
      // Keep maximum initial requests to 25
      maxInitialRequests: 25,
      // A chunk should be at least 20kb before using splitChunks
      minSize: 20000
    },
    minimizer: [
      new CompressionPlugin({
        filename: "[path][base].gz",
        algorithm: "gzip",
        test: /\.js$|\.css$|\.html$/,
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CompressionPlugin({
        filename: "[path][base].br",
        algorithm: "brotliCompress",
        test: /\.(js|css|html|png|svg)$/,
        compressionOptions: {
          params: {
            [zlib.constants.BROTLI_PARAM_QUALITY]: 11,
          },
        },
        threshold: 10240,
        minRatio: 0.8,
      }),
      new CssMinimizerPlugin(),
      new TerserPlugin(),
    ]
  },
  plugins: [
    new webpack.NoEmitOnErrorsPlugin(),
    new CleanWebpackPlugin(),
    new WebpackManifestPlugin({
      fileName: "webpack_manifest.json",
      publicPath: ""
    }),
    new MiniCssExtractPlugin({
      filename: "css/[name].css",
      chunkFilename: "css/[id]-[contenthash].css",
    }),
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/favicon.ico",
          to: "img",
        },
      ],
    }),
    // These are files used in the progressive web app manifest only, so they wouldn't
    //  be processed without this.
    new CopyWebpackPlugin({
      patterns: [
        {
          from: "assets/img/logo_small_*.png",
          to: "img/[name]-[contenthash].png",
          toType: "template"
        },
      ],
    }),
    new MomentLocalesPlugin({
      localesToKeep: ["de", "en"],
    })
  ],
  resolve: {
    extensions: [".js", ".jsx", ".tsx", ".ts"],
    modules: [
      resolve(__dirname, "src"),
      resolve(__dirname, "node_modules"),
    ]
  },
};

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文