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>
This commit is contained in:
80
host/inject.go
Normal file
80
host/inject.go
Normal file
@@ -0,0 +1,80 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user