Can not serve WASM from template

Short story:
I have an html file that is using WebAssembly file, everything is running smoothly if the html is loaded as a static file using Golang server, but if the html is loaded as a template then the WebAssembly file is not running, and I get the error:

localhost/:1 Uncaught (in promise) CompileError: WebAssembly.compile(): expected magic word 00 61 73 6d, found 3c 21 44 4f @+0

Long story:
I have the below html file:

<!DOCTYPE html>
<html>
<head>
  <title>Golang WebAssembly Example</title>
</head>
<body>
  <button onclick="handleClick()">Click me</button>
  <button onclick="callHandleData()">handleData</button>
  <button onclick="notifyme()">notifyme</button>
  <div id="result"></div>
  <script src="/script/wasm_exec.js"></script>
  <script>
    const go = new Go();
    let wasm;

    fetch('main.wasm').then(response =>
      response.arrayBuffer()
    ).then(bytes => WebAssembly.instantiate(bytes, go.importObject)).then(result => {
      wasm = result.instance;
      go.run(wasm); // This will start the Go runtime
    })

    function callHandleData() {
        let data = handleData(46, "Hello, World!");
        // Parse the data using JSON.parse  // Parse the data using JSON.parse
        let parsedData = JSON.parse(data);  // Double stringify for safety

        // Access the text property from the parsed object
        let text = parsedData.text;
        let num = parsedData.number;
        alert(data)
        alert(text + " What is the "+ num);  // Or use text for further manipulation
    }
  </script>
</body>
</html>

I’m trying to load it from a template in a Golang as below, but I’m getting the mentioned error:

package main

import (
	"html/template"
	"net/http"
	"path/filepath"
)

func handler(w http.ResponseWriter, r *http.Request) {

	baseTmpl, err := template.ParseFiles("wasm.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	err = baseTmpl.ExecuteTemplate(w, "wasm.html", nil) // Pass nil as data
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func main() {
	http.HandleFunc("/", handler)

	// Serve static files
	http.HandleFunc("/script/", func(w http.ResponseWriter, r *http.Request) {
		filePath := r.URL.Path[len("/script/"):]
		if filepath.Ext(filePath) == ".wasm" {
			w.Header().Set("Content-Type", "application/wasm")
		}
		http.ServeFile(w, r, "script/"+filePath)
	})

	http.ListenAndServe(":8080", nil)
}

I’m not sure what happened, once I wrote the below things run smoothly:

	// Serve wasm_exec.js file with correct MIME type
	http.HandleFunc("/scripts/wasm_exec.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/javascript")
		http.ServeFile(w, r, "/scripts/wasm_exec.js")
	})

	// Serve main.wasm file with correct MIME type
	http.HandleFunc("/wasm/main.wasm", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/wasm")
		http.ServeFile(w, r, "/wasm/main.wasm")
	})

Later on I committed them, and the app still running smoothly!!

Below is my full code:

// main.go
package main

import (
	"html/template"
	"log"
	"net/http"
)

// Define user access levels
var userAccessLevels = map[string][]string{
	"manager":           {"edit"},
	"auditor":           {"readonly"},
	"limitedAccessUser": {"readonly"},
	"supervisor":        {"edit", "no-access"},
}

// Sample data for the table
var tableData = []struct {
	ID    int
	Name  string
	Role  string
	Email string
}{
	{1, "John Doe", "manager", "john@example.com"},
	{2, "Jane Doe", "auditor", "jane@example.com"},
	{3, "Alice", "limitedAccessUser", "alice@example.com"},
	{4, "Bob", "supervisor", "bob@example.com"},
}

func main() {
	// using GET /{$} mean / only, but using GET / mean / and anything after it which may cause routes conflict
	http.Handle("GET /home", loggingMiddleware(http.HandlerFunc(homePage)))
	http.Handle("GET /base", loggingMiddleware(http.HandlerFunc(baseHandler)))

	http.HandleFunc("GET /{$}", handler)

	// // Serve wasm_exec.js file with correct MIME type
	// http.HandleFunc("/scripts/wasm_exec.js", func(w http.ResponseWriter, r *http.Request) {
	// 	w.Header().Set("Content-Type", "application/javascript")
	// 	http.ServeFile(w, r, "/scripts/wasm_exec.js")
	// })

	// // Serve main.wasm file with correct MIME type
	// http.HandleFunc("/wasm/main.wasm", func(w http.ResponseWriter, r *http.Request) {
	// 	w.Header().Set("Content-Type", "application/wasm")
	// 	http.ServeFile(w, r, "/wasm/main.wasm")
	// })

	//http.Handle("/", http.FileServer(http.Dir("./static")))
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("static"))))

	// Start the server with CSP headers
	http.ListenAndServe(":8080", nil)
}

func baseHandler(w http.ResponseWriter, r *http.Request) {
	// Define templates
	//t := template.Must(template.ParseFiles("template/base.html", "template/body.html"))
	t, err := template.ParseFiles("template/base.html", "template/body.html") //)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	// Execute base template
	if err := t.Execute(w, nil); err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
}

func handler(w http.ResponseWriter, r *http.Request) {
	t, err := template.ParseFiles("template/index.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	t.Execute(w, nil)
}

func homePage(w http.ResponseWriter, r *http.Request) {
	userType := "supervisor"
	accessLevels, ok := userAccessLevels[userType]
	if !ok {
		http.Error(w, "Invalid user type", http.StatusBadRequest)
		return
	}
	log.Println(accessLevels)

	t, err := template.ParseFiles("template/homePage.html")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	data := struct {
		Title   string
		Message string
		Roles   []string
		Access  map[string]string
		Table   []struct {
			ID    int
			Name  string
			Role  string
			Email string
		}
	}{
		Title:   "Welcome",
		Message: "This is the Home Page",
		Roles:   accessLevels,
		Access:  makeAccessMap(accessLevels),
		Table:   tableData,
	}
	t.Execute(w, data)
}

func loggingMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Printf("Request received: %s %s", r.Method, r.URL.Path)
		next.ServeHTTP(w, r)
		log.Println("Request handled successfully")
	})
}

func makeAccessMap(accessLevels []string) map[string]string {
	accessMap := make(map[string]string)
	for _, level := range accessLevels {
		accessMap[level] = level
	}
	return accessMap
}

And the Web Assembly files are built from:

// base.go
//go:build js && wasm

package main

import (
	"encoding/json"
	"fmt"
	"syscall/js"
)

var (
	global   js.Value
	document js.Value
)

func main() {
	global = js.Global()
	document = global.Get("document") // Cache document for efficiency

	global.Set("handleData", js.FuncOf(handleData))
	global.Set("notifyme", js.FuncOf(notifyme))
	//	global.Set("clickCallback", js.FuncOf(clickCallback))

	select {} // Block main thread
}

func handleData(this js.Value, args []js.Value) interface{} {
	if len(args) < 2 {
		return nil // Handle invalid number of arguments gracefully
	}

	num := args[0].Int()
	text := args[1].String()

	data := map[string]interface{}{
		"number": num,
		"text":   text,
	}

	jsonData, err := json.Marshal(data)
	if err != nil {
		// Handle potential marshalling errors (optional)
		log(fmt.Sprintf("Error: %v", err))
		return nil
	}

	addElement(jsonData)
	return string(jsonData)
}

func notifyme(this js.Value, args []js.Value) interface{} {
	script := `
	new function() {
		let data = "just a reminder";
		let para = document.createElement("p");
		let node = document.createTextNode(data);
		para.appendChild(node);
		document.body.appendChild(para);
	}();
    `
	global.Call("eval", script)
	log("a Just reminder should be printed at the page")

	return nil
}

func log(message string) {
	js.Global().Get("console").Call("log", message)
}

func createElement(tagName string) js.Value {
	return js.Global().Get("document").Call("createElement", tagName)
}

func addElement(jsonData []byte) {
	var parsedData map[string]interface{}
	err := json.Unmarshal(jsonData, &parsedData)
	if err != nil {
		// Handle potential unmarshalling errors (optional)
		return
	}

	text := parsedData["text"].(string)
	num := parsedData["number"].(float64)

	node := document.Call("createTextNode", fmt.Sprintf("%v: What is the %.2f?", text, num)) // Format number with 2 decimal places

	para := createElement("p")
	para.Call("appendChild", node)

	// Create a js.Func object for clickCallback
	clickCallbackFunc := js.FuncOf(clickCallback)
	addEventListener(para, "click", clickCallbackFunc)

	body := document.Get("body")
	body.Call("appendChild", para)
}

func addEventListener(el js.Value, event string, callback js.Func) {
	el.Call("addEventListener", event, callback)
}

func clickCallback(this js.Value, args []js.Value) interface{} {
	log("Para had been clicked")
	return nil
}

// $env:GOOS = "js"
// $env:GOARCH = "wasm"
// $CGO_LDFLAGS="-Wl,-LTO"
// go build -ldflags="-s -w" -o base.wasm -tags js,wasm base.go
// go build -o base.wasm -tags js,wasm base.go

And

// body.go
//go:build js && wasm

package main

import (
	"syscall/js"
)

var (
	global js.Value
)

func init() {
	global = js.Global()
}

func main() {
	global.Set("handleClick", js.FuncOf(handleClick))

	select {} // Block main thread
}

func handleClick(this js.Value, args []js.Value) interface{} {
	alert("Hello, world!") // Use custom alert function
	return nil
}

func alert(message string) {
	global.Call("alert", message)
}

// $env:GOOS = "js"
// $env:GOARCH = "wasm"
// $CGO_LDFLAGS="-Wl,-LTO"
// go build -ldflags="-s -w" -o body.wasm -tags js,wasm body.go
// go build -o body.wasm -tags js,wasm body.go

The generated wasm files had been moved to the folder static/wasm

And in the static/script folder I have:

// wasm_load.js
const modules = {};
let wasm;

async function loadWasmModule(module) {
    modules[module] = new Go();

    try {
        const response = await fetch(`/static/wasm/${module}.wasm`);
        const bytes = await response.arrayBuffer();
        const result_1 = await WebAssembly.instantiate(bytes, modules[module].importObject);
        wasm = result_1.instance;
        modules[module].run(wasm);
    } catch (error) {
        console.error(`Failed to load wasm module ${module}:`, error);
    }
}

The template files used are:

// template/base.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Base Template</title>
    <script src="/static/scripts/wasm_exec.js"></script>
    <script src="/static/scripts/wasm_load.js"></script>
</head>
<body>
    <header>
        <h1>Welcome to My Website</h1>
    </header>
    
    <main>
        {{template "content" .}}
    </main>
    
    <footer>
        <p>&copy; 2024 My Website. All rights reserved.</p>
        <button onclick="notifyme()">notifyme</button>
    </footer>

</body>

<script>
    loadWasmModule('base')
</script>

</html>

And

//template/body.js
{{define "content"}}
    <h2>Body Content</h2>
    <button onclick="handleClick()">Click me</button>
    <p>This is the content of the body template.</p>
    <button onclick="callHandleData()">handleData</button>


<script>
  loadWasmModule('body')
  function callHandleData() {
    let data = handleData(46, "Hello, World!");
    // Parse the data using JSON.parse  // Parse the data using JSON.parse
    let parsedData = JSON.parse(data);  // Double stringify for safety

    // Access the text property from the parsed object
    let text = parsedData.text;
    let num = parsedData.number;
    alert(data)
    alert(text + " What is the "+ num);  // Or use text for further manipulation
  }
</script>

{{end}}

And

//template.index.html
{{define "content"}}
    <h2>Body Content</h2>
    <button onclick="handleClick()">Click me</button>
    <p>This is the content of the body template.</p>
    <button onclick="callHandleData()">handleData</button>


<script>
  loadWasmModule('body')
  function callHandleData() {
    let data = handleData(46, "Hello, World!");
    // Parse the data using JSON.parse  // Parse the data using JSON.parse
    let parsedData = JSON.parse(data);  // Double stringify for safety

    // Access the text property from the parsed object
    let text = parsedData.text;
    let num = parsedData.number;
    alert(data)
    alert(text + " What is the "+ num);  // Or use text for further manipulation
  }
</script>

{{end}}

And

//template/homePage.html
<!DOCTYPE html>
<html>
<head>
    <title>{{.Title}}</title>
    <style>
        /* CSS styles here */
        table {
            border-collapse: collapse;
            width: 100%;
            table-layout: fixed;
        }

        th, td {
            padding: 3px; /* Adjusted padding for narrower rows */
            border: 1px solid #ddd;
            text-align: left;
            width: 150px; /* Set fixed width for cells */
            overflow: hidden; /* Hide overflow content */
            white-space: nowrap; /* Prevent wrapping */
        }

        tr:nth-child(even) {
            background-color: #f2f2f2;
        }

        /* tr:hover {
            background-color: #ddd;
        } */

        th {
            background-color: #4CAF50;
            color: white;
        }

        .editable {
            background-color: #fff;
        }

        .editable:focus {
            background-color: #f0f0f0;
        }

        /* Define individual widths for columns */
        .col1 {
            width: 100px; /* Width for ID column */
        }

        .col2 {
            width: 200px; /* Width for Name column */
        }

        .col3 {
            width: 150px; /* Width for Role column */
        }

        .col4 {
            width: 250px; /* Width for Email column */
        }

        /* Style for the first column */
        .first-column {
            background-color: #ffcccb; /* Light red background color */
        }

        /* Hover effect for table cells */
        tbody tr:hover td,
        tbody tr td:hover {
            background-color: #ffe5cc; /* Light orange background color */
        }

        @media screen and (max-width: 600px) {
            table {
                border: 0;
            }
            th, td {
                border: none;
                display: block;
                padding: 3px; /* Adjusted padding for narrower rows */
                width: 100%; /* Set width to 100% for full width in smaller screens */
            }
            th {
                background-color: #4CAF50;
                color: white;
                text-align: left;
            }
        }
    </style>
</head>
<body>
    <h1>{{.Title}}</h1>
    <p>{{.Message}}</p>
    <p>User roles are: {{.Roles}}</p>

    <div>
        {{range $role, $access := .Access}}
            <p>Role: {{$role}}, Access: {{$access}}</p>
        {{end}}
    </div>

    {{ $readOnly := false }}
    {{ $isVisible := true }}
    {{ range .Roles }}
        {{ if eq . "no-access" }}
            {{ $isVisible = false }}
        {{ end }}
        {{ if eq . "readonly" }}
            {{ $readOnly = true }}
        {{ end }}
    {{ end }}

    {{ if $isVisible }}
        <div data-access="{{if $readOnly}} readonly {{end}}">
            <input type="text" {{if $readOnly}} readonly {{end}} placeholder="Enter text here">
        </div>
    {{ else }}
        <p>This content is not accessible.</p>
    {{ end }}


    
    <table border="1">
        <tr>
            <th class="col1">ID</th>
            <th class="col2">Name</th>
            <th class="col3">Role</th>
            {{ if $isVisible }}
            <th class="col4" data-access="{{if $readOnly}}readonly{{end}}">Email</th>
            {{ end }}
        </tr>
        {{range .Table}}
            <tr>
                <td contenteditable="true" class="editable col1 first-column">{{.ID}}</td>
                <td contenteditable="true" class="editable col2">{{.Name}}</td>
                <td contenteditable="true" class="editable col3">{{.Role}}</td>
                {{ if $isVisible }}
                <td contenteditable="true" class="editable col4" data-access="{{if $readOnly}}readonly{{end}}">{{.Email}}</td>
                {{ end }}
            </tr>
        {{end}}
    </table>

    <script>
        // Add event listeners to each editable cell
        const editableCells = document.querySelectorAll('.editable');
        editableCells.forEach(cell => {
            cell.addEventListener('input', function() {
                // Add your logic here to handle input changes
                console.log('Cell content changed:', this.textContent);
            });
        });

                // Add event listeners to each cell in the table body
                const cells = document.querySelectorAll('tbody td');
        cells.forEach(cell => {
            cell.addEventListener('mouseover', function() {
                // Add highlighted class to the row
                this.parentNode.classList.add('highlighted');

                // Add highlighted class to all cells in the same column
                const index = Array.from(this.parentNode.children).indexOf(this);
                const allRows = document.querySelectorAll('tbody tr');
                allRows.forEach(row => {
                    row.children[index].classList.add('highlighted');
                });
            });

            cell.addEventListener('mouseout', function() {
                // Remove highlighted class from the row
                this.parentNode.classList.remove('highlighted');

                // Remove highlighted class from all cells in the same column
                const index = Array.from(this.parentNode.children).indexOf(this);
                const allRows = document.querySelectorAll('tbody tr');
                allRows.forEach(row => {
                    row.children[index].classList.remove('highlighted');
                });
            });
        });
    </script>
</body>
</html>