Files
iris/host/inject.go
Hugo Nijhuis 89efc9b41d Add hot reload for development
Implement automatic rebuild and browser reload during development:

- File watcher monitors .go files for changes with configurable extensions
- Builder compiles Go source to WASM on file changes
- LiveReload WebSocket server notifies connected browsers to reload
- DevServer combines all components for easy development setup
- HTML injection adds reload script automatically

Usage:
  dev := host.NewDevServer("public", "index.html", ".", "public/app.wasm")
  dev.ListenAndServe(":8080")

Closes #9

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 16:34:16 +00:00

81 lines
2.3 KiB
Go

package host
import (
"bytes"
"strings"
)
// ReloadScript is the JavaScript code injected into HTML pages for hot reload.
const ReloadScript = `<script>
(function() {
var wsUrl = 'ws://' + window.location.host + '/__livereload';
var ws;
var reconnectAttempts = 0;
var maxReconnectDelay = 5000;
function connect() {
ws = new WebSocket(wsUrl);
ws.onopen = function() {
console.log('[iris] Hot reload connected');
reconnectAttempts = 0;
};
ws.onmessage = function(event) {
if (event.data === 'reload') {
console.log('[iris] Reloading...');
window.location.reload();
}
};
ws.onclose = function() {
var delay = Math.min(1000 * Math.pow(2, reconnectAttempts), maxReconnectDelay);
reconnectAttempts++;
console.log('[iris] Connection lost. Reconnecting in ' + delay + 'ms...');
setTimeout(connect, delay);
};
ws.onerror = function() {
ws.close();
};
}
connect();
})();
</script>`
// InjectReloadScript injects the live reload script into HTML content.
// The script is inserted just before the closing </body> tag.
func InjectReloadScript(html []byte) []byte {
// Try to inject before </body>
bodyEnd := bytes.LastIndex(html, []byte("</body>"))
if bodyEnd != -1 {
result := make([]byte, 0, len(html)+len(ReloadScript))
result = append(result, html[:bodyEnd]...)
result = append(result, []byte(ReloadScript)...)
result = append(result, html[bodyEnd:]...)
return result
}
// Try to inject before </html>
htmlEnd := bytes.LastIndex(html, []byte("</html>"))
if htmlEnd != -1 {
result := make([]byte, 0, len(html)+len(ReloadScript))
result = append(result, html[:htmlEnd]...)
result = append(result, []byte(ReloadScript)...)
result = append(result, html[htmlEnd:]...)
return result
}
// Append at the end as fallback
result := make([]byte, 0, len(html)+len(ReloadScript))
result = append(result, html...)
result = append(result, []byte(ReloadScript)...)
return result
}
// IsHTMLContent checks if the content type indicates HTML.
func IsHTMLContent(contentType string) bool {
return strings.Contains(contentType, "text/html")
}