Worker 🍩
This repository contains a Service Worker designed to enhance web application performance and reliability through advanced caching, offline support, and a unique strategy for handling dynamic CSS imports originating from JavaScript modules.
- Asset Caching: Implements multiple caching strategies:
- Core Cache (
CACHE_CORE
): Stores essential application shell files and critical scripts (like/Application/
,Register.js
,Load.js
). Uses a network-first strategy for navigation requests to ensure users get the latest page structure if online, falling back to the cache when offline. Pre-caches essential assets on install. - Asset Cache (
CACHE_ASSET
): Stores static application assets (/Static/Application/*
), including JavaScript, images, and the actual CSS files. Uses a cache-first strategy for fast loading. Also stores the dynamically generated JavaScript modules used for CSS loading (see below). - Shim Cache (
CACHE_SHIM
): Designated for shim/polyfill assets.
- Core Cache (
- Offline Support: Leverages the caches to allow the application shell and cached assets to function offline.
- Dynamic CSS Loading: Intercepts JavaScript
import
statements for specific CSS files and responds with a JavaScript module that triggers the loading of the actual CSS via a standard<link>
tag. - Automatic Updates: Detects when a new version of the Service Worker is
activated and prompts the client (via
Register.js
) to reload the page, ensuring the user gets the latest application version seamlessly. - Client Control Management: The
Register.js
script ensures the Service Worker gains control of the page, potentially reloading the page once after the initial registration if necessary.
This worker implements a specific strategy to handle dynamic CSS imports from
JavaScript modules (e.g., import './some-styles.css';
) located under the
/Static/Application/
path. Instead of relying on postMessage
coordination,
it directly responds to the initial import request with JavaScript code that
initiates the standard browser CSS loading mechanism.
The Workflow:
- Initial JS Import: A JavaScript module in your application attempts to
import a CSS file located under
/Static/Application/
(e.g.,/Static/Application/CodeEditorLand/component.css
). - Service Worker Intercept #1: The worker's
fetch
listener intercepts this request. Because the URL matches the pattern/Static/Application/*.css
and doesn't contain the special?Skip=Intercept
parameter, it proceeds with the CSS handling logic. - Service Worker Responds with JS: The worker immediately responds to
the fetch request with a dynamically generated JavaScript module
(
Content-Type: application/javascript; charset=utf-8
). The content of this module is similar to:This JavaScript response is then cached inwindow._LOAD_CSS_WORKER("/Static/Application/CodeEditorLand/component.css"); export default {};
CACHE_ASSET
using the original CSS request URL as the key. - Browser Executes JS: The browser receives and executes this JavaScript
module. The
export default {};
satisfies the expectation of the originalimport
statement. - Client Function Call: The executed JavaScript calls the globally
available
window._LOAD_CSS_WORKER
function (which must be defined beforehand by includingLoad.js
). - Client Modifies URL & Creates
<link>
: The_LOAD_CSS_WORKER
function appends the?Skip=Intercept
query parameter to the received CSS URL (e.g.,/Static/Application/CodeEditorLand/component.css?Skip=Intercept
). It then creates a standard<link rel="stylesheet">
tag, setting itshref
to this modified URL, and appends it to the document's<head>
. - Browser Fetches CSS: The browser sees the new
<link>
tag and initiates a second fetch request for the CSS file, this time using the URL with the?Skip=Intercept
parameter. - Service Worker Intercept #2: The worker intercepts this second request.
- Service Worker Serves CSS: The worker detects the
?Skip=Intercept
parameter. It bypasses the JS generation logic and proceeds to fetch the actual CSS content using a cache-first strategy againstCACHE_ASSET
(looking for the URL including the parameter in the cache, or fetching from the network). It responds with the real CSS content (Content-Type: text/css
). - Browser Applies Styles: The browser receives the actual CSS and applies the styles as expected.
This two-step fetch process, initiated by the SW's JavaScript response and
distinguished by the Skip=Intercept
parameter, allows the initial JavaScript
import to resolve quickly while triggering the standard browser mechanism for
loading the actual CSS styles without causing infinite interception loops.
This example shows how to integrate the necessary client-side scripts and the
Service Worker registration within an HTML page (.html
file).
index.html
(or your main layout/page):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>My App with Service Worker CSS Loading</title>
<!-- Optional: Favicon, other meta tags -->
<link href="/favicon.ico" rel="icon" />
<!--
IMPORTANT: Load the CSS Loader script EARLY.
This script defines the global function window._LOAD_CSS_WORKER
needed by the Service Worker's JS response.
It needs to run before your main app script tries to import CSS.
-->
<script src="/Worker/CSS/Load.js" type="module"></script>
<!--
Define the path to the Service Worker file so Register.js can find it.
This script block should come *before* Register.js.
Ensure this path correctly points to where your Worker.js is served.
-->
<script>
// Set the path relative to the web root where Worker.js will be served.
window._WORKER = "/Worker.js"; // Default is "/Worker.js"
</script>
<!--
Register the Service Worker.
This script handles registration, listens for updates from the SW
(triggering reloads), and manages ensuring the SW controls the page.
It uses the window._WORKER path defined above and registers with scope '/Application'.
-->
<script src="/Worker/Register.js" type="module"></script>
<!-- Optional: Load any other critical CSS or JS needed before the main app -->
<link href="/styles/base.css" rel="stylesheet" />
</head>
<body>
<header>
<h1>Application Header</h1>
</header>
<main>
<p>Loading application...</p>
<!-- Your main application might render into a specific div -->
<div id="app-container"></div>
</main>
<footer>
<p>Application Footer</p>
</footer>
<!--
Load your main application script LAST.
Any dynamic import '/Static/Application/some-component.css'
inside this script or its dependencies will trigger the
Service Worker interception and JS module response described above.
-->
<script src="/scripts/main-app.js" type="module"></script>
</body>
</html>
See CHANGELOG.md
for a history of changes to this component.
This project is funded through NGI0 Commons Fund, a fund established by NLnet with financial support from the European Commission's Next Generation Internet program. Learn more at the NLnet project page.
Land | PlayForm | NLnet | NGI0 Commons Fund |
---|---|---|---|