Hot-Swap Internals
The hotSwap() function handles three asset types. Each uses a different strategy for in-place replacement.
Scripts
Regular scripts (<script src="...">) are replaced by creating a new <script> element with the same content but without a src attribute. The old element is swapped out.
Module scripts (<script type="module" src="...">) get type="module" on the replacement. If the original had integrity or crossorigin, those are copied.
Each replacement is tagged with a data-hot-file attribute set to the filename. On subsequent updates (same file, new content), the library finds the tagged element and updates its textContent in place instead of creating a new element. This keeps the script active and avoids re-execution in some cases.
// First update: creates a new script element
<script data-hot-file="main.js">/* new code */</script>
// Second update: finds the tagged element, updates textContent
Stylesheets
CSS hot-swap uses CSSStyleSheet.replaceSync with adoptedStyleSheets. This approach is synchronous, CSP-safe (no inline <style> or data: URIs), and avoids relative url() breakage.
On the first update for a given file:
- The original
<link rel="stylesheet">is disabled (link.disabled = true) - A new
CSSStyleSheetis created and added todocument.adoptedStyleSheets - The sheet is tracked in a
Map<string, CSSStyleSheet>by filename
On subsequent updates, the same sheet is found in the map and replaceSync() is called with the new content. No DOM manipulation.
const hotSheets = new Map<string, CSSStyleSheet>();
function swapStylesheet(file, content) {
let sheet = hotSheets.get(file);
if (!sheet) {
const link = matchAttr('link[rel="stylesheet"]', "href", file);
if (link) link.disabled = true;
sheet = new CSSStyleSheet();
document.adoptedStyleSheets = [...document.adoptedStyleSheets, sheet];
hotSheets.set(file, sheet);
}
sheet.replaceSync(content);
}
Images
Images are replaced by converting the file content to a base64 data URI and setting it on matching <img>, <source>, and <link rel="icon"> elements.
Matching uses querySelectorAll with a partial src / href match on the filename. Every matching element on the page gets the new data URI.
Unknown types
Files with unrecognized extensions return { swapped: false, type: "unknown" }. The caller can handle them or fall through to a page reload.
Fallback
If every file in an applyUpdate call returns swapped: false, the page reloads as a safety net.
Next Steps
- Client Library: Full API reference
- Manifest: How files and modules are declared