Skip to content

Commit 8954684

Browse files
committed
Add Hot Swap for JavaScript modules
1 parent 90be24c commit 8954684

File tree

7 files changed

+72
-18
lines changed

7 files changed

+72
-18
lines changed

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use.
77
## Features
88

99
* Live-reload via `ibazel` and a custom live-reload script.
10+
* Hot swappable JavaScript modules.
1011
* Custom routes for any dependency in the BUILD rule's `data` attribute.
1112

1213
## Pre-requisites
@@ -68,3 +69,13 @@ ibazel run //:serve
6869
```
6970

7071
Whenever a file in the `data` attribute is changed, the server will be live-reloaded.
72+
73+
## Hot Swap JavaScript Modules.
74+
75+
To hot swap a JavaScript module, add the a data attribute on the `<script>` tag in the HTML file. For example:
76+
77+
```
78+
<script type="module" data-hot-swap="true" src="/scripts/bundle.js"></script>
79+
```
80+
81+
When the server is live-reloaded, the browser will hot swap the JavaScript module, and the hot page itself will not reload.

devserver/devserver_loader.js

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
function maybeReplaceJavaScript(src) {
2+
const script = document.querySelector(`script[src="${src}"]`);
3+
if (script) {
4+
const newScript = document.createElement("script");
5+
newScript.src = src;
6+
newScript.dataset.hotSwap = "true";
7+
script.parentNode.replaceChild(newScript, script);
8+
}
9+
}
10+
11+
function liveReload(path) {
12+
console.log("Reloading due to change in " + path);
13+
window.location.reload();
14+
}
15+
116
setInterval(() => {
217
fetch("/devserver/manifest")
318
.then(function (response) {
@@ -9,11 +24,21 @@ setInterval(() => {
924
return;
1025
}
1126

12-
for (let key in manifest) {
13-
if (manifest[key] !== __DEVSERVER_MANIFEST__[key]) {
14-
console.log("Reloading due to change in " + key);
15-
window.location.reload();
27+
for (const path in manifest) {
28+
if (manifest[path] !== __DEVSERVER_MANIFEST__[path]) {
29+
if (path.endsWith(".js")) {
30+
let script = document.querySelector(`script[src="${path}"]`);
31+
if (script && script.dataset.hotSwap === "true") {
32+
maybeReplaceJavaScript(path);
33+
__DEVSERVER_MANIFEST__ = manifest;
34+
} else {
35+
liveReload(path);
36+
}
37+
} else {
38+
liveReload(path);
39+
}
1640
}
1741
}
1842
})
1943
}, 1000);
44+

examples/js_serve/BUILD

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
load("//devserver:defs.bzl", "devserver")
22

33
filegroup(
4-
name = "hello_world",
5-
srcs = ["hello_world.js"],
4+
name = "hot_swap",
5+
srcs = ["hot_swap.js"],
6+
visibility = ["//visibility:public"],
7+
)
8+
9+
filegroup(
10+
name = "counter",
11+
srcs = ["counter.js"],
612
visibility = ["//visibility:public"],
713
)
814

@@ -18,8 +24,9 @@ devserver(
1824
workspace_name = "rules_devserver",
1925
static_files = {
2026
"/": [":index", "index.html"],
21-
"/js/hello_world.js": [":hello_world", "hello_world.js"]
27+
"/js/hot_swap.js": [":hot_swap", "hot_swap.js"],
28+
"/js/counter.js": [":counter", "counter.js"]
2229
},
2330
debug = True,
24-
data = [":index", ":hello_world"]
31+
data = [":index", ":hot_swap", ":counter"]
2532
)

examples/js_serve/counter.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
(function () {
2+
const container = document.getElementById('counter-container');
3+
if (container) {
4+
let count = 0;
5+
container.innerHTML = `Count: ${count}`;
6+
setInterval(() => {
7+
count++;
8+
container.innerHTML = `Count: ${count}`;
9+
}, 1000);
10+
}
11+
})();

examples/js_serve/hello_world.js

Lines changed: 0 additions & 8 deletions
This file was deleted.

examples/js_serve/hot_swap.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
(function () {
2+
const container = document.getElementById('hot-swap-container');
3+
if (container) {
4+
container.innerHTML = 'Filled by hot_swap.js';
5+
}
6+
})();

examples/js_serve/index.html

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
<head>
44
<title>JavaScript Serve Example</title>
5-
<script defer src="/js/hello_world.js"></script>
5+
<script defer src="/js/hot_swap.js" data-hot-swap="true"></script>
6+
<script defer src="/js/counter.js"></script>
67
</head>
78

89
<body>
910
<h1>JavaScript Serve Example</h1>
10-
<div id="container"></div>
11+
<div id="hot-swap-container"></div>
12+
<div id="counter-container"></div>
1113

1214
</html>

0 commit comments

Comments
 (0)