Repository: a-h/templ
Pull request #1048 (2025-01-14 15:06:21)

docs: clarify when live reload might fail due to middleware implementation
Hello, I didn't realize I haven't been benefiting from live reload despite setting it up in my Makefile. After digging about today it turns out my use of https://github.com/go-swiss/compress Brotli middleware was causing issues, and that is not the case when using other gzip or middleware implementation. Without diving deeper, it seems weird that this compression middleware creates a problem, because it is using the same Brotli package as templ's under the hood for proxy.go. Probably a case of implementation where compressed handler got written to ResponseWriter and flushed, so there was nothing to inspect when templ got its chance.

Regardless, I think it'd be a good idea to clarify upfront that the Content-Type is important for the JS script insertion and hint at middleware implementation being an issue. 


Patch:
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index ef2ce91..cf03af0 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -14,7 +14,7 @@ Please make sure you're using the latest version of the templ CLI (`go install g
 A clear and concise description of what the bug is.
 
 **To Reproduce**
-A small, self-container, complete reproduction, uploaded to a Github repo, containing the minimum amount of files required to reproduce the behaviour, along with a list of commands that need to be run. Keep it simple.
+A small, self-container, complete reproduction, uploaded to a GitHub repo, containing the minimum amount of files required to reproduce the behaviour, along with a list of commands that need to be run. Keep it simple.
 
 **Expected behavior**
 A clear and concise description of what you expected to happen.
diff --git a/.version b/.version
index d9bec1e..b835087 100644
--- a/.version
+++ b/.version
@@ -1,1 +1,1 @@
-0.3.822
\ No newline at end of file
+0.3.828
diff --git a/README.md b/README.md
index 60482fd..60d2f67 100644
--- a/README.md
+++ b/README.md
@@ -156,7 +156,7 @@ git diff --exit-code
 
 ### push-release-tag
 
-Push a semantic version number to Github to trigger the release process.
+Push a semantic version number to GitHub to trigger the release process.
 
 ```sh
 ./push-tag.sh
diff --git a/SECURITY.md b/SECURITY.md
index 8241f55..e8c820e 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -6,4 +6,4 @@ The latest version of templ is supported.
 
 ## Reporting a Vulnerability
 
-Use the "Security" tab in Github and fill out the "Report a vulnerability" form.
+Use the "Security" tab in GitHub and fill out the "Report a vulnerability" form.
diff --git a/cmd/templ/generatecmd/cmd.go b/cmd/templ/generatecmd/cmd.go
index 246e51c..169cc06 100644
--- a/cmd/templ/generatecmd/cmd.go
+++ b/cmd/templ/generatecmd/cmd.go
@@ -226,14 +226,18 @@ func (cmd Generate) Run(ctx context.Context) (err error) {
 				if err != nil {
 					errs <- err
 				}
-				if r.GoUpdated || r.TextUpdated {
-					postGeneration <- &GenerationEvent{
-						Event:       event,
-						Updated:     r.Updated,
-						GoUpdated:   r.GoUpdated,
-						TextUpdated: r.TextUpdated,
-					}
+				if !(r.GoUpdated || r.TextUpdated) {
+					cmd.Log.Debug("File not updated", slog.String("file", event.Name))
+					return
+				}
+				e := &GenerationEvent{
+					Event:       event,
+					Updated:     r.Updated,
+					GoUpdated:   r.GoUpdated,
+					TextUpdated: r.TextUpdated,
 				}
+				cmd.Log.Debug("File updated", slog.String("file", event.Name))
+				postGeneration <- e
 			}(event)
 		}
 		// Wait for all events to be processed before closing.
diff --git a/cmd/templ/generatecmd/eventhandler.go b/cmd/templ/generatecmd/eventhandler.go
index 32800fb..93ce332 100644
--- a/cmd/templ/generatecmd/eventhandler.go
+++ b/cmd/templ/generatecmd/eventhandler.go
@@ -282,16 +282,16 @@ func (h *FSEventHandler) generate(ctx context.Context, fileName string) (result
 			if err = os.WriteFile(txtFileName, []byte(joined), 0o644); err != nil {
 				return result, nil, fmt.Errorf("failed to write string literal file %q: %w", txtFileName, err)
 			}
+		}
 
-			// Check whether the change would require a recompilation to take effect.
-			h.fileNameToOutputMutex.Lock()
-			defer h.fileNameToOutputMutex.Unlock()
-			previous := h.fileNameToOutput[fileName]
-			if generator.HasChanged(previous, generatorOutput) {
-				result.GoUpdated = true
-			}
-			h.fileNameToOutput[fileName] = generatorOutput
+		// Check whether the change would require a recompilation to take effect.
+		h.fileNameToOutputMutex.Lock()
+		defer h.fileNameToOutputMutex.Unlock()
+		previous := h.fileNameToOutput[fileName]
+		if generator.HasChanged(previous, generatorOutput) {
+			result.GoUpdated = true
 		}
+		h.fileNameToOutput[fileName] = generatorOutput
 	}
 
 	parsedDiagnostics, err := parser.Diagnose(t)
diff --git a/docs/docs/02-quick-start/01-installation.md b/docs/docs/02-quick-start/01-installation.md
index d53c574..7a2d20e 100644
--- a/docs/docs/02-quick-start/01-installation.md
+++ b/docs/docs/02-quick-start/01-installation.md
@@ -8,7 +8,7 @@ With Go 1.23 or greater installed, run:
 go install github.com/a-h/templ/cmd/templ@latest
 ```
 
-## Github binaries
+## GitHub binaries
 
 Download the latest release from https://github.com/a-h/templ/releases/latest
 
diff --git a/docs/docs/03-syntax-and-usage/03-attributes.md b/docs/docs/03-syntax-and-usage/03-attributes.md
index 8aabd09..f2c6b75 100644
--- a/docs/docs/03-syntax-and-usage/03-attributes.md
+++ b/docs/docs/03-syntax-and-usage/03-attributes.md
@@ -225,7 +225,7 @@ templ Button(text string) {
 ```
 
 ```html title="Output"
-<script type="text/javascript">
+<script>
  function __templ_withParameters_1056(a, b, c){console.log(a, b, c);}function __templ_withoutParameters_6bbf(){alert("hello");}
 </script>
 <button onclick="__templ_withParameters_1056("test","Say hello",123)" onmouseover="__templ_withoutParameters_6bbf()" type="button">
diff --git a/docs/docs/03-syntax-and-usage/12-script-templates.md b/docs/docs/03-syntax-and-usage/12-script-templates.md
index 12e2847..ce40557 100644
--- a/docs/docs/03-syntax-and-usage/12-script-templates.md
+++ b/docs/docs/03-syntax-and-usage/12-script-templates.md
@@ -6,7 +6,7 @@ Use standard `<script>` tags, and standard HTML attributes to run JavaScript on
 
 ```templ
 templ body() {
-  <script type="text/javascript">
+  <script>
     function handleClick(event) {
       alert(event + ' clicked');
     }
@@ -49,7 +49,7 @@ HTML element `on*` attributes pass an event object to the function. To pass the
 :::
 
 ```templ title="input.templ"
-<script type="text/javascript">
+<script>
 	function clickHandler(event, message) {
 		alert(message);
 		event.preventDefault();
@@ -61,7 +61,7 @@ HTML element `on*` attributes pass an event object to the function. To pass the
 The output would be:
 
 ```html title="output.html"
-<script type="text/javascript">
+<script>
 	function clickHandler(event, message) {
 		alert(message);
 		event.preventDefault();
@@ -87,7 +87,7 @@ templ InitializeClientSideScripts(data CustomType) {
 This will output a `<script>` tag that calls the `functionToCall` function with the `Name` and `Age` properties of the `data` object.
 
 ```html title="output.html"
-<script type="text/javascript">
+<script>
   functionToCall("John", 42);
 </script>
 ```
@@ -169,7 +169,7 @@ var helloHandle = templ.NewOnceHandle()
 templ hello(label, name string) {
   // This script is only rendered once per HTTP request.
   @helloHandle.Once() {
-    <script type="text/javascript">
+    <script>
       function hello(name) {
         alert('Hello, ' + name + '!');
       }
@@ -177,7 +177,7 @@ templ hello(label, name string) {
   }
   <div>
     <input type="button" value={ label } data-name={ name }/>
-    <script type="text/javascript">
+    <script>
       // To prevent the variables from leaking into the global scope,
       // this script is wrapped in an IIFE (Immediately Invoked Function Expression).
       (() => {
@@ -213,7 +213,7 @@ var surrealHandle = templ.NewOnceHandle()
 
 templ hello(label, name string) {
   @helloHandle.Once() {
-    <script type="text/javascript">
+    <script>
       function hello(name) {
         alert('Hello, ' + name + '!');
       }
@@ -224,7 +224,7 @@ templ hello(label, name string) {
   }
   <div>
     <input type="button" value={ label } data-name={ name }/>
-    <script type="text/javascript">
+    <script>
       // me("-") returns the previous sibling element.
       me("-").addEventListener('click', function() {
         let name = this.getAttribute('data-name');
@@ -458,7 +458,7 @@ templ page(content string) {
 }
 ```
 
-The data passed into the Javascript funtion will be JSON encoded, which then can be used inside the function.
+The data passed into the Javascript function will be JSON encoded, which then can be used inside the function.
 
 ```go title="main.go"
 package main
@@ -492,9 +492,9 @@ After building and running the executable, running `curl http://localhost:8080/`
 ```html title="Output"
 <html>
 	<body>
-		<script type="text/javascript">function __templ_printToConsole_5a85(content){console.log(content)}</script>
-		<script type="text/javascript">__templ_printToConsole_5a85("2023-11-11 01:01:40.983381358 +0000 UTC")</script>
-		<script type="text/javascript">__templ_printToConsole_5a85("Again: 2023-11-11 01:01:40.983381358 +0000 UTC")</script>
+		<script>function __templ_printToConsole_5a85(content){console.log(content)}</script>
+		<script>__templ_printToConsole_5a85("2023-11-11 01:01:40.983381358 +0000 UTC")</script>
+		<script>__templ_printToConsole_5a85("Again: 2023-11-11 01:01:40.983381358 +0000 UTC")</script>
 	</body>
 </html>
 ```
diff --git a/docs/docs/03-syntax-and-usage/17-using-react-with-templ.md b/docs/docs/03-syntax-and-usage/17-using-react-with-templ.md
index 6b1d1a5..e898b08 100644
--- a/docs/docs/03-syntax-and-usage/17-using-react-with-templ.md
+++ b/docs/docs/03-syntax-and-usage/17-using-react-with-templ.md
@@ -188,7 +188,7 @@ import "fmt"
 
 templ Hello(name string) {
 	<div data-name={ name }>
-		<script type="text/javascript">
+		<script>
 			bundle.renderHello(document.currentScript.closest('div'));
 		</script>
 	</div>
@@ -243,19 +243,19 @@ The HTML that's rendered is:
     <script src="static/index.js"></script>
 
     <div data-name="Alice">
-      <script type="text/javascript">
+      <script>
         // Place the React component into the parent div.
         bundle.renderHello(document.currentScript.closest('div'));
       </script>
     </div>
     <div data-name="Bob">
-      <script type="text/javascript">
+      <script>
         // Place the React component into the parent div.
 	bundle.renderHello(document.currentScript.closest('div'));
       </script>
     </div>
     <div data-name="Charlie">
-      <script type="text/javascript">
+      <script>
         // Place the React component into the parent div.
 	bundle.renderHello(document.currentScript.closest('div'));
       </script>
diff --git a/docs/docs/03-syntax-and-usage/18-render-once.md b/docs/docs/03-syntax-and-usage/18-render-once.md
index d2c42cd..39f332d 100644
--- a/docs/docs/03-syntax-and-usage/18-render-once.md
+++ b/docs/docs/03-syntax-and-usage/18-render-once.md
@@ -19,7 +19,7 @@ var helloHandle = templ.NewOnceHandle()
 
 templ hello(label, name string) {
   @helloHandle.Once() {
-    <script type="text/javascript">
+    <script>
       function hello(name) {
         alert('Hello, ' + name + '!');
       }
@@ -35,7 +35,7 @@ templ page() {
 ```
 
 ```html title="Output"
-<script type="text/javascript">
+<script>
   function hello(name) {
     alert('Hello, ' + name + '!');
   }
diff --git a/docs/docs/05-server-side-rendering/04-datastar.md b/docs/docs/05-server-side-rendering/04-datastar.md
index e71e96a..e098327 100644
--- a/docs/docs/05-server-side-rendering/04-datastar.md
+++ b/docs/docs/05-server-side-rendering/04-datastar.md
@@ -2,7 +2,7 @@
 
 [Datastar](https://data-star.dev) is a hypermedia framework that is similar to [HTMX](htmx).
 
-Datastar can be used to selectively replace content within a web page by combining fine grained reactive signals with SSE. It's geared primarily to real time applications where you'd normally reach for a SPA framework such as React/Vue/Svelte.
+Datastar can selectively replace content within a web page by combining fine-grained reactive signals with SSE. It's geared primarily to real-time applications where you'd normally reach for a SPA framework such as React/Vue/Svelte.
 
 ## Usage
 
@@ -13,7 +13,7 @@ Using Datastar requires:
 
 ## Installation
 
-Datastar comes out of the box with templ components to speed up development. You can use `@datastar.ScriptCDNLatest()` or `ScriptCDNVersion(version string)` to include the latest version of the Datastar library in your HTML.
+Datastar is included with Templ components out of the box to speed up development. You can use `@datastar.ScriptCDNLatest()` or `ScriptCDNVersion(version string)` to include the latest version of the Datastar library in your HTML.
 
 :::info
 Advanced Datastar installation and usage help is covered in the user guide at https://data-star.dev.
@@ -35,12 +35,14 @@ We are going to modify the [templ counter example](example-counter-application)
 
 ### Frontend
 
-First, define a HTML some with two buttons. One to update a global state, and one to update a per-user state.
+First, define some HTML with two buttons. One to update a global state, and one to update a per-user state.
 
 ```templ title="components.templ"
 package site
 
-type TemplCounterStore struct {
+import datastar "github.com/starfederation/datastar/sdk/go"
+
+type TemplCounterSignals struct {
 	Global uint32 `json:"global"`
 	User   uint32 `json:"user"`
 }
@@ -48,12 +50,13 @@ type TemplCounterStore struct {
 templ templCounterExampleButtons() {
 	<div>
 		<button
-			data-on-click="$$post('/examples/templ_counter/increment/global')"
+			data-on-click="@post('/examples/templ_counter/increment/global')" 
 		>
 			Increment Global
 		</button>
 		<button
-			data-on-click="$$post('/examples/templ_counter/increment/user')"
+			data-on-click={ datastar.PostSSE('/examples/templ_counter/increment/user') }
+			<!-- Alternative: Using Datastar SDK sugar--> 
 		>
 			Increment User
 		</button>
@@ -73,10 +76,10 @@ templ templCounterExampleCounts() {
 	</div>
 }
 
-templ templCounterExampleInitialContents(store TemplCounterStore) {
+templ templCounterExampleInitialContents(signals TemplCounterSignals) {
 	<div
 		id="container"
-		data-store={ templ.JSONString(store) }
+		data-signals={ templ.JSONString(signals) }
 	>
 		@templCounterExampleButtons()
 		@templCounterExampleCounts()
@@ -85,13 +88,13 @@ templ templCounterExampleInitialContents(store TemplCounterStore) {
 ```
 
 :::tip
-Note that Datastar doesn't promote the use of forms, because they are ill-suited to nested reactive contents. Instead it sends all[^1] reactive state (as JSON) to the server on each request. This means far less bookkeeping and more predictable state management.
+Note that Datastar doesn't promote the use of forms because they are ill-suited to nested reactive content. Instead, it sends all[^1] reactive state (as JSON) to the server on each request. This means far less bookkeeping and more predictable state management.
 :::
 
 :::note
-`data-store` is a special attribute that Datastar uses to store the initial state of the page. The contents will turn into signals that can be used to update the page.
+`data-signals` is a special attribute that Datastar uses to merge one or more signals into the existing signals. In the example, we store $global and $user when we initially render the container. 
 
-`data-on-click="$$post('/examples/templ_counter/increment/global')"` is an attribute expression that says "when this element is clicked, send a POST request to the server to the specified URL". The `$$post` is an action that is a sandboxed function that knows about things like signals.
+`data-on-click="@post('/examples/templ_counter/increment/global')"` is an attribute expression that says "When this element is clicked, send a POST request to the server to the specified URL". The `@post` is an action that is a sandboxed function that knows about things like signals.
 
 `data-text="$global"` is an attribute expression that says "replace the contents of this element with the value of the `global` signal in the store". This is a reactive signal that will update the page when the value changes, which we'll see in a moment.
 :::
@@ -108,12 +111,13 @@ import (
 	"sync/atomic"
 
 	"github.com/Jeffail/gabs/v2"
-	"github.com/delaneyj/datastar"
 	"github.com/go-chi/chi/v5"
 	"github.com/gorilla/sessions"
+	datastar "github.com/starfederation/datastar/sdk/go"
 )
 
-func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.Store) error {
+func setupExamplesTemplCounter(examplesRouter chi.Router, sessionSignals sessions.Store) error {
+
 	var globalCounter atomic.Uint32
 	const (
 		sessionKey = "templ_counter"
@@ -121,7 +125,7 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
 	)
 
 	userVal := func(r *http.Request) (uint32, *sessions.Session, error) {
-		sess, err := sessionStore.Get(r, sessionKey)
+		sess, err := sessionSignals.Get(r, sessionKey)
 		if err != nil {
 			return 0, nil, err
 		}
@@ -139,17 +143,17 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 		}
 
-		store := TemplCounterStore{
+		signals := TemplCounterSignals{
 			Global: globalCounter.Load(),
 			User:   userVal,
 		}
 
-		sse := datastar.NewSSE(w, r)
-		datastar.RenderFragmentTempl(sse, templCounterExampleInitialContents(store))
+		c := templCounterExampleInitialContents(signals)
+		datastar.NewSSE(w, r).MergeFragmentTempl(c)
 	})
 
-	updateGlobal := func(store *gabs.Container) {
-		store.Set(globalCounter.Add(1), "global")
+	updateGlobal := func(signals *gabs.Container) {
+		signals.Set(globalCounter.Add(1), "global")
 	}
 
 	examplesRouter.Route("/templ_counter/increment", func(incrementRouter chi.Router) {
@@ -157,8 +161,7 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
 			update := gabs.New()
 			updateGlobal(update)
 
-			sse := datastar.NewSSE(w, r)
-			datastar.PatchStore(sse, update)
+			datastar.NewSSE(w, r).MarshalAndMergeSignals(update)
 		})
 
 		incrementRouter.Post("/user", func(w http.ResponseWriter, r *http.Request) {
@@ -177,30 +180,28 @@ func setupExamplesTemplCounter(examplesRouter chi.Router, sessionStore sessions.
 			updateGlobal(update)
 			update.Set(val, "user")
 
-			sse := datastar.NewSSE(w, r)
-			datastar.PatchStore(sse, update)
+			datastar.NewSSE(w, r).MarshalAndMergeSignals(update)
 		})
 	})
 
 	return nil
-}
-```
+}```
 
-The `atomic.Uint32` type is used to store the global state. The `userVal` function is a helper that retrieves the user's session state. The `updateGlobal` function increments the global state.
+The `atomic.Uint32` type stores the global state. The `userVal` function is a helper that retrieves the user's session state. The `updateGlobal` function increments the global state.
 
 :::note
-In this example, the global state is stored in RAM, and will be lost when the web server reboots. To support load-balanced web servers, and stateless function deployments, consider storing the state in a data store such as [NATS KV](https://docs.nats.io/using-nats/developer/develop_jetstream/kv).
+In this example, the global state is stored in RAM and will be lost when the web server reboots. To support load-balanced web servers and stateless function deployments, consider storing the state in a data store such as [NATS KV](https://docs.nats.io/using-nats/developer/develop_jetstream/kv).
 :::
 
 ### Per-user session state
 
-In a HTTP application, per-user state information is partitioned by a HTTP cookie. Cookies that identify a user while they're using a site are known as "session cookies". When the HTTP handler receives a request, it can read the session ID of the user from the cookie and retrieve any required state.
+In an HTTP application, per-user state information is partitioned by an HTTP cookie. Cookies that identify a user while they're using a site are known as "session cookies". When the HTTP handler receives a request, it can read the session ID of the user from the cookie and retrieve any required state.
 
-### Signal only patching
+### Signal-only patching
 
-Since the page's elements aren't changing dynamically, we can use the `datastar.PatchStore` function to send only the signals that have changed. This is a more efficient way to update the page without even needing to send HTML fragments.
+Since the page's elements aren't changing dynamically, we can use the `MarshalAndMergeSignals` function to send only the signals that have changed. This is a more efficient way to update the page without even needing to send HTML fragments.
 
 :::tip
-Datastar will merge updates to the store similar to a JSON merge patch. This means you can do dynamic partial updates to the store and the page will update accordingly. [Gabs](https://pkg.go.dev/github.com/Jeffail/gabs/v2#section-readme) is used here to handle dynamic JSON in Go.
+Datastar will merge updates to signals similar to a JSON merge patch. This means you can do dynamic partial updates to the store and the page will update accordingly. [Gabs](https://pkg.go.dev/github.com/Jeffail/gabs/v2#section-readme) is used here to handle dynamic JSON in Go.
 
-[^1]: You can control the data that is sent to the server by prefixing local signals with `_`. This will prevent them from being sent to the server on every request.
+[^1]: You can control the data sent to the server by prefixing local signals with `_`. This will prevent them from being sent to the server on every request.
diff --git a/docs/docs/09-developer-tools/02-ide-support.md b/docs/docs/09-developer-tools/02-ide-support.md
index d624901..732e73c 100644
--- a/docs/docs/09-developer-tools/02-ide-support.md
+++ b/docs/docs/09-developer-tools/02-ide-support.md
@@ -213,9 +213,9 @@ end
 
 ### Troubleshooting
 
-If you cannot run `:TSInstall templ`, ensure you have an up-to-date version of [tree-sitter](https://github.com/nvim-treesitter/nvim-treesitter). The [package for templ](https://github.com/vrischmann/tree-sitter-templ) was [added to the main tree-sitter repositry](https://github.com/nvim-treesitter/nvim-treesitter/pull/5667) so you shouldn't need to install a separate plugin for it.
+If you cannot run `:TSInstall templ`, ensure you have an up-to-date version of [tree-sitter](https://github.com/nvim-treesitter/nvim-treesitter). The [package for templ](https://github.com/vrischmann/tree-sitter-templ) was [added to the main tree-sitter repository](https://github.com/nvim-treesitter/nvim-treesitter/pull/5667) so you shouldn't need to install a separate plugin for it.
 
-If you still don't get syntax highlighting after it's installed, try running `:TSBufEnable highlight`. If you find that you need to do this every time you open a .templ file, you can run this autocmd to do it for your neovim configuation.
+If you still don't get syntax highlighting after it's installed, try running `:TSBufEnable highlight`. If you find that you need to do this every time you open a .templ file, you can run this autocmd to do it for your neovim configuration.
 
 ```lua
 vim.api.nvim_create_autocmd("BufEnter", { pattern = "*.templ", callback = function() vim.cmd("TSBufEnable highlight") end }) 
diff --git a/docs/docs/09-developer-tools/03-live-reload.md b/docs/docs/09-developer-tools/03-live-reload.md
index 510b970..bee532b 100644
--- a/docs/docs/09-developer-tools/03-live-reload.md
+++ b/docs/docs/09-developer-tools/03-live-reload.md
@@ -6,6 +6,7 @@ To access a Go web application that uses templ in a web browser, a few things mu
 2. The Go code must start a web server on a port, e.g. (`http.ListenAndServe("localhost:8080", nil)`.
 3. The Go program must be ran, e.g. by running `go run .`.
 4. The web browser must access or reload the page, e.g. `http://localhost:8080`.
+5. Content-Type must be text/html. 
 
 If the `*.templ` files change, #1 and #2 must be ran.
 
@@ -19,7 +20,7 @@ To re-run your app automatically, add the `--cmd` argument to `templ generate`,
 
 To trigger your web browser to reload automatically (without pressing F5), set the `--proxy` argument (#4) to point at your app, and browse to the proxy address (default `http://localhost:7331`).
 
-The `--proxy` argument starts a HTTP proxy which proxies requests to your app. For example, if your app runs on port 8080, you would use `--proxy="http://localhost:8080"`. The proxy inserts client-side JavaScript before the `</body>` tag that will cause the browser to reload the window when the app is restarted instead of you having to reload the page manually. Note that the html being served by the webserver MUST have a `<body>` tag, otherwise there will be no javascript injection thus making the browser not reload automatically.
+The `--proxy` argument starts a HTTP proxy which proxies requests to your app. For example, if your app runs on port 8080, you would use `--proxy="http://localhost:8080"`. The proxy inserts client-side JavaScript before the `</body>` tag that will cause the browser to reload the window when the app is restarted instead of you having to reload the page manually. Note that the html being served by the webserver MUST have a `<body>` tag, otherwise there will be no javascript injection thus making the browser not reload automatically. In addition, the script might not be inserted if templ cannot read and modify the http response, e.g. due to middleware implementation.   
 
 By default, the proxy binds to `127.0.0.1`. You can use `--proxybind` to bind to another address, e.g., `--proxybind="0.0.0.0"`.
 
@@ -106,7 +107,7 @@ templ generate --notify-proxy --proxybind="localhost" --proxyport="8080"
 wgo -file=.go -file=.templ -xfile=_templ.go templ generate :: go run main.go
 ```
 
-To avoid a continous reloading files ending with `_templ.go` should be skipped via `-xfile`.
+To avoid a continuous reloading files ending with `_templ.go` should be skipped via `-xfile`.
 
 ## Alternative 2: air
 
diff --git a/docs/docs/09-developer-tools/05-llm.md b/docs/docs/09-developer-tools/05-llm.md
index 393a459..7031e9f 100644
--- a/docs/docs/09-developer-tools/05-llm.md
+++ b/docs/docs/09-developer-tools/05-llm.md
@@ -1,6 +1,6 @@
 # Coding assistants / LLMs
 
-To provide AI coding assistants such as Github Copilot, Cursor or similar with help on how to write templ code, the templ project maintains a single file containing documentation for LLMs to read.
+To provide AI coding assistants such as GitHub Copilot, Cursor or similar with help on how to write templ code, the templ project maintains a single file containing documentation for LLMs to read.
 
 You can find the file at `https://templ.guide/llms.md`.
 
@@ -8,6 +8,6 @@ You can find the file at `https://templ.guide/llms.md`.
 
 ### https://github.com/CopilotC-Nvim/CopilotChat.nvim
 
-CopilotChat is a plugin for Neovim that provides a chat interface for Github Copilot. It allows you to ask Copilot questions and get responses in real-time.
+CopilotChat is a plugin for Neovim that provides a chat interface for GitHub Copilot. It allows you to ask Copilot questions and get responses in real-time.
 
 Use the URL feature to load `https://templ.guide/llms.md`.
diff --git a/docs/docs/10-security/01-injection-attacks.md b/docs/docs/10-security/01-injection-attacks.md
index 9a1f832..4cb45c8 100644
--- a/docs/docs/10-security/01-injection-attacks.md
+++ b/docs/docs/10-security/01-injection-attacks.md
@@ -6,7 +6,7 @@ templ is designed to prevent user-provided data from being used to inject vulner
 
 ```html
 templ Example() {
-  <script type="text/javascript">
+  <script>
     function showAlert() {
       alert("hello");
     }
diff --git a/docs/docs/10-security/02-content-security-policy.md b/docs/docs/10-security/02-content-security-policy.md
index 6ba5e04..36a9e58 100644
--- a/docs/docs/10-security/02-content-security-policy.md
+++ b/docs/docs/10-security/02-content-security-policy.md
@@ -75,12 +75,12 @@ func main() {
 ```
 
 ```html title="Output"
-<script type="text/javascript" nonce="randomly generated nonce">
+<script nonce="randomly generated nonce">
   function __templ_onLoad_5a85() {
     alert("Hello, world!")
   }
 </script>
-<script type="text/javascript" nonce="randomly generated nonce">
+<script nonce="randomly generated nonce">
   __templ_onLoad_5a85()
 </script>
 ```
diff --git a/docs/docs/10-security/03-code-signing.md b/docs/docs/10-security/03-code-signing.md
index 4738501..c6cfb75 100644
--- a/docs/docs/10-security/03-code-signing.md
+++ b/docs/docs/10-security/03-code-signing.md
@@ -1,6 +1,6 @@
 # Code signing
 
-Binaries are created by the Github Actions workflow at https://github.com/a-h/templ/blob/main/.github/workflows/release.yml
+Binaries are created by the GitHub Actions workflow at https://github.com/a-h/templ/blob/main/.github/workflows/release.yml
 
 Binaries are signed by cosign. The public key is stored in the repository at https://github.com/a-h/templ/blob/main/cosign.pub
 
diff --git a/docs/docs/13-experimental/02-urlbuilder.md b/docs/docs/13-experimental/02-urlbuilder.md
index 2848eba..b3ddf61 100644
--- a/docs/docs/13-experimental/02-urlbuilder.md
+++ b/docs/docs/13-experimental/02-urlbuilder.md
@@ -24,7 +24,7 @@ templ component(o Order) {
 }
 ```
 
-See [URL Attribures](/syntax-and-usage/attributes#url-attributes) for more information.
+See [URL Attributes](/syntax-and-usage/attributes#url-attributes) for more information.
 
 ## Feedback
 
diff --git a/docs/docs/14-help-and-community/index.md b/docs/docs/14-help-and-community/index.md
index daf12e2..d3a2fae 100644
--- a/docs/docs/14-help-and-community/index.md
+++ b/docs/docs/14-help-and-community/index.md
@@ -8,6 +8,6 @@ Use the #templ channel in the Gopher Slack community.
 
 https://invite.slack.golangbridge.org/
 
-## Github Discussion
+## GitHub Discussion
 
 https://github.com/a-h/templ/discussions
diff --git a/examples/integration-react/components.templ b/examples/integration-react/components.templ
index 0b1b6e0..54a7aa1 100644
--- a/examples/integration-react/components.templ
+++ b/examples/integration-react/components.templ
@@ -2,7 +2,7 @@ package main
 
 templ Hello(name string) {
 	<div data-name={ name }>
-		<script type="text/javascript">
+		<script>
 			// Place the React component into the parent div.
 			bundle.renderHello(document.currentScript.closest('div'));
 		</script>
diff --git a/examples/integration-react/components_templ.go b/examples/integration-react/components_templ.go
index ee38da5..44efd85 100644
--- a/examples/integration-react/components_templ.go
+++ b/examples/integration-react/components_templ.go
@@ -41,7 +41,7 @@ func Hello(name string) templ.Component {
 		if templ_7745c5c3_Err != nil {
 			return templ_7745c5c3_Err
 		}
-		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><script type=\"text/javascript\">\n\t\t\t// Place the React component into the parent div.\n\t\t\tbundle.renderHello(document.currentScript.closest('div'));\n\t\t</script></div>")
+		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "\"><script>\n\t\t\t// Place the React component into the parent div.\n\t\t\tbundle.renderHello(document.currentScript.closest('div'));\n\t\t</script></div>")
 		if templ_7745c5c3_Err != nil {
 			return templ_7745c5c3_Err
 		}
diff --git a/flake.nix b/flake.nix
index 1795976..63f402a 100644
--- a/flake.nix
+++ b/flake.nix
@@ -37,7 +37,7 @@
             name = "templ";
             subPackages = [ "cmd/templ" ];
             src = gitignore.lib.gitignoreSource ./.;
-            vendorHash = "sha256-ipLn52MsgX7KQOJixYcwMR9TCeHz55kQQ7fgkIgnu7w=";
+            vendorHash = "sha256-PuxZZPrr4/ar6Pic60HPt1Wob5usCWfthhrzzp9iswQ=";
             CGO_ENABLED = 0;
             flags = [
               "-trimpath"
diff --git a/generator/generator.go b/generator/generator.go
index 3b8e316..48202cb 100644
--- a/generator/generator.go
+++ b/generator/generator.go
@@ -973,7 +973,7 @@ func (g *generator) writeElement(indentLevel int, n parser.Element) (err error)
 		if err = g.writeElementCSS(indentLevel, attrs); err != nil {
 			return err
 		}
-		// <script type="text/javascript"></script>
+		// <script></script>
 		if err = g.writeElementScript(indentLevel, attrs); err != nil {
 			return err
 		}
@@ -1013,7 +1013,7 @@ func (g *generator) writeAttributeCSS(indentLevel int, attr parser.ExpressionAtt
 	}
 	// Create a class name for the style.
 	// The expression can either be expecting a templ.Classes call, or an expression that returns
-	// var templ_7745c5c3_CSSClassess = []any{
+	// var templ_7745c5c3_CSSClasses = []any{
 	classesName := g.createVariableName()
 	if _, err = g.w.WriteIndent(indentLevel, "var "+classesName+" = []any{"); err != nil {
 		return
@@ -1028,7 +1028,7 @@ func (g *generator) writeAttributeCSS(indentLevel int, attr parser.ExpressionAtt
 		return
 	}
 	// Render the CSS before the element if required.
-	// templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_CSSClassess...)
+	// templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_CSSClasses...)
 	if _, err = g.w.WriteIndent(indentLevel, "templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, "+classesName+"...)\n"); err != nil {
 		return
 	}
@@ -1356,7 +1356,7 @@ func (g *generator) writeRawElement(indentLevel int, n parser.RawElement) (err e
 			return err
 		}
 	} else {
-		// <script type="text/javascript"></script>
+		// <script></script>
 		if err = g.writeElementScript(indentLevel, n.Attributes); err != nil {
 			return err
 		}
diff --git a/go.mod b/go.mod
index 3aca6ae..7c0f7b0 100644
--- a/go.mod
+++ b/go.mod
@@ -7,7 +7,7 @@ toolchain go1.23.3
 require (
 	github.com/PuerkitoBio/goquery v1.10.1
 	github.com/a-h/htmlformat v0.0.0-20231108124658-5bd994fe268e
-	github.com/a-h/parse v0.0.0-20240121214402-3caf7543159a
+	github.com/a-h/parse v0.0.0-20250122154542-74294addb73e
 	github.com/a-h/protocol v0.0.0-20240704131721-1e461c188041
 	github.com/andybalholm/brotli v1.1.0
 	github.com/cenkalti/backoff/v4 v4.3.0
diff --git a/go.sum b/go.sum
index 39b4fb2..7778ef2 100644
--- a/go.sum
+++ b/go.sum
@@ -4,6 +4,8 @@ github.com/a-h/htmlformat v0.0.0-20231108124658-5bd994fe268e h1:Eog54DQpku7NpPNf
 github.com/a-h/htmlformat v0.0.0-20231108124658-5bd994fe268e/go.mod h1:FMIm5afKmEfarNbIXOaPHFY8X7fo+fRQB6I9MPG2nB0=
 github.com/a-h/parse v0.0.0-20240121214402-3caf7543159a h1:vlmAfVwFK9sRpDlJyuHY8htP+KfGHB2VH02u0SoIufk=
 github.com/a-h/parse v0.0.0-20240121214402-3caf7543159a/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
+github.com/a-h/parse v0.0.0-20250122154542-74294addb73e h1:HjVbSQHy+dnlS6C3XajZ69NYAb5jbGNfHanvm1+iYlo=
+github.com/a-h/parse v0.0.0-20250122154542-74294addb73e/go.mod h1:3mnrkvGpurZ4ZrTDbYU84xhwXW2TjTKShSwjRi2ihfQ=
 github.com/a-h/protocol v0.0.0-20240704131721-1e461c188041 h1:2enlC41iOwWklx9ZUqpQygsNAG6KIm3uMMUXzBJw5jA=
 github.com/a-h/protocol v0.0.0-20240704131721-1e461c188041/go.mod h1:Gm0KywveHnkiIhqFSMZglXwWZRQICg3KDWLYdglv/d8=
 github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
diff --git a/parser/v2/ifexpressionparser.go b/parser/v2/ifexpressionparser.go
index f8cddb0..926f6c5 100644
--- a/parser/v2/ifexpressionparser.go
+++ b/parser/v2/ifexpressionparser.go
@@ -19,7 +19,7 @@ func (ifExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error) {
 		return r, false, nil
 	}
 
-	// Parse the Go if expresion.
+	// Parse the Go if expression.
 	if r.Expression, err = parseGo("if", pi, goexpression.If); err != nil {
 		return r, false, err
 	}
@@ -78,7 +78,7 @@ func (elseIfExpressionParser) Parse(pi *parse.Input) (r ElseIfExpression, ok boo
 
 	// Rewind to the start of the `if` statement.
 	pi.Seek(pi.Index() - 2)
-	// Parse the Go if expresion.
+	// Parse the Go if expression.
 	if r.Expression, err = parseGo("else if", pi, goexpression.If); err != nil {
 		return r, false, err
 	}
diff --git a/parser/v2/switchexpressionparser.go b/parser/v2/switchexpressionparser.go
index f21e4bf..cbdb02c 100644
--- a/parser/v2/switchexpressionparser.go
+++ b/parser/v2/switchexpressionparser.go
@@ -19,7 +19,7 @@ func (switchExpressionParser) Parse(pi *parse.Input) (n Node, ok bool, err error
 		return
 	}
 
-	// Parse the Go switch expresion.
+	// Parse the Go switch expression.
 	if r.Expression, err = parseGo("switch", pi, goexpression.Switch); err != nil {
 		return r, false, err
 	}
@@ -67,7 +67,7 @@ var caseExpressionStartParser = parse.Func(func(pi *parse.Input) (r Expression,
 		pi.Seek(start)
 		return r, false, nil
 	}
-	// Parse the Go expresion.
+	// Parse the Go expression.
 	if r, err = parseGo("case", pi, goexpression.Case); err != nil {
 		return r, false, err
 	}
diff --git a/scripttemplate.go b/scripttemplate.go
index 9603cc8..0aafa0b 100644
--- a/scripttemplate.go
+++ b/scripttemplate.go
@@ -49,7 +49,7 @@ func writeScriptHeader(ctx context.Context, w io.Writer) (err error) {
 	if nonce := GetNonce(ctx); nonce != "" {
 		nonceAttr = " nonce=\"" + EscapeString(nonce) + "\""
 	}
-	_, err = fmt.Fprintf(w, `<script type="text/javascript"%s>`, nonceAttr)
+	_, err = fmt.Fprintf(w, `<script%s>`, nonceAttr)
 	return err
 }
 


Test patch:
diff --git a/cmd/templ/lspcmd/lsp_test.go b/cmd/templ/lspcmd/lsp_test.go
index a3c807a..1767ee3 100644
--- a/cmd/templ/lspcmd/lsp_test.go
+++ b/cmd/templ/lspcmd/lsp_test.go
@@ -719,11 +719,11 @@ func TestDocumentSymbol(t *testing.T) {
 				}
 			}
 
-			expectdSlice, err := sliceToAnySlice(test.expect)
+			expectedSlice, err := sliceToAnySlice(test.expect)
 			if err != nil {
 				t.Errorf("failed to convert expect to any slice: %v", err)
 			}
-			diff := cmp.Diff(expectdSlice, actual)
+			diff := cmp.Diff(expectedSlice, actual)
 			if diff != "" {
 				t.Errorf("unexpected document symbol: %v", diff)
 			}
@@ -843,7 +843,7 @@ func Setup(ctx context.Context, log *zap.Logger) (clientCtx context.Context, app
 	var wg sync.WaitGroup
 	var cmdErr error
 
-	// Copy from the LSP to the CLient, and vice versa.
+	// Copy from the LSP to the Client, and vice versa.
 	fromClient, toLSP := io.Pipe()
 	fromLSP, toClient := io.Pipe()
 	clientStream := jsonrpc2.NewStream(newStdRwc(log, "clientStream", toLSP, fromLSP))
diff --git a/generator/test-css-usage/expected.html b/generator/test-css-usage/expected.html
index ff33193..d488fa1 100644
--- a/generator/test-css-usage/expected.html
+++ b/generator/test-css-usage/expected.html
@@ -11,7 +11,7 @@
 <div class="a c">Maps can be used to determine if a class should be added or not.</div>
 <style type="text/css">.e_739d4573{font-size:14pt;}</style>
 <div class="a c e_739d4573">KV can be used to conditionally set classes.</div>
-<div class="bg-violet-500 hover:bg-red-600 hover:bg-sky-700 text-[#50d71e] w-[calc(100%-4rem)">Psuedo attributes and complex class names are supported.</div>
+<div class="bg-violet-500 hover:bg-red-600 hover:bg-sky-700 text-[#50d71e] w-[calc(100%-4rem)">Pseudo attributes and complex class names are supported.</div>
 <div class="a&#34; onClick=&#34;alert(&#39;hello&#39;)&#34;">
 	Class names are HTML escaped.
 </div>
diff --git a/generator/test-css-usage/template.templ b/generator/test-css-usage/template.templ
index 9773448..0af1f19 100644
--- a/generator/test-css-usage/template.templ
+++ b/generator/test-css-usage/template.templ
@@ -54,8 +54,8 @@ templ KVCanBeUsedToConditionallySetClasses() {
 }
 
 // Pseudo attributes can be used without any special syntax.
-templ PsuedoAttributesAndComplexClassNamesAreSupported() {
-	<div class={ "bg-violet-500", "hover:bg-red-600", "hover:bg-sky-700", "text-[#50d71e]", "w-[calc(100%-4rem)" }>Psuedo attributes and complex class names are supported.</div>
+templ PseudoAttributesAndComplexClassNamesAreSupported() {
+	<div class={ "bg-violet-500", "hover:bg-red-600", "hover:bg-sky-700", "text-[#50d71e]", "w-[calc(100%-4rem)" }>Pseudo attributes and complex class names are supported.</div>
 }
 
 // Class names are HTML escaped.
@@ -89,7 +89,7 @@ templ TestComponent() {
 	@CSSComponentsAndConstantsAreSupported()
 	@MapsCanBeUsedToConditionallySetClasses()
 	@KVCanBeUsedToConditionallySetClasses()
-	@PsuedoAttributesAndComplexClassNamesAreSupported()
+	@PseudoAttributesAndComplexClassNamesAreSupported()
 	@ClassNamesAreHTMLEscaped()
 	@CSSComponentsCanBeUsedWithArguments()
 	@Rotate(45)
diff --git a/generator/test-css-usage/template_templ.go b/generator/test-css-usage/template_templ.go
index 47cd749..f93f3f2 100644
--- a/generator/test-css-usage/template_templ.go
+++ b/generator/test-css-usage/template_templ.go
@@ -290,7 +290,7 @@ func KVCanBeUsedToConditionallySetClasses() templ.Component {
 }
 
 // Pseudo attributes can be used without any special syntax.
-func PsuedoAttributesAndComplexClassNamesAreSupported() templ.Component {
+func PseudoAttributesAndComplexClassNamesAreSupported() templ.Component {
 	return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) {
 		templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context
 		if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil {
@@ -329,7 +329,7 @@ func PsuedoAttributesAndComplexClassNamesAreSupported() templ.Component {
 		if templ_7745c5c3_Err != nil {
 			return templ_7745c5c3_Err
 		}
-		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">Psuedo attributes and complex class names are supported.</div>")
+		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">Pseudo attributes and complex class names are supported.</div>")
 		if templ_7745c5c3_Err != nil {
 			return templ_7745c5c3_Err
 		}
@@ -564,7 +564,7 @@ func TestComponent() templ.Component {
 		if templ_7745c5c3_Err != nil {
 			return templ_7745c5c3_Err
 		}
-		templ_7745c5c3_Err = PsuedoAttributesAndComplexClassNamesAreSupported().Render(ctx, templ_7745c5c3_Buffer)
+		templ_7745c5c3_Err = PseudoAttributesAndComplexClassNamesAreSupported().Render(ctx, templ_7745c5c3_Buffer)
 		if templ_7745c5c3_Err != nil {
 			return templ_7745c5c3_Err
 		}
diff --git a/generator/test-js-unsafe-usage/expected.html b/generator/test-js-unsafe-usage/expected.html
index 4f802c3..d41f233 100644
--- a/generator/test-js-unsafe-usage/expected.html
+++ b/generator/test-js-unsafe-usage/expected.html
@@ -1,6 +1,6 @@
 <button onClick="anythingILike('blah')">
  Click me
 </button>
-<script type="text/javascript">
+<script>
  // Arbitrary JS code
 </script>
diff --git a/generator/test-js-usage/expected.html b/generator/test-js-usage/expected.html
index b81f5d4..fd433e5 100644
--- a/generator/test-js-usage/expected.html
+++ b/generator/test-js-usage/expected.html
@@ -1,7 +1,7 @@
 <button onclick="alert(&#34;Hello, World!&#34;)">
  Click me
 </button>
-<script type="text/javascript">
+<script>
  function customAlert(msg, date) {
 				alert(msg + " " + date);
 			}
@@ -12,7 +12,7 @@
 <button onclick="customAlert(&#34;Hello, custom alert 2: &#34;,&#34;2020-01-01T00:00:00Z&#34;)">
  Click me
 </button>
-<script type="text/javascript">
+<script>
  customAlert("Runs on page load","2020-01-01T00:00:00Z")
 </script>
 <script>
diff --git a/generator/test-js-usage/template.templ b/generator/test-js-usage/template.templ
index 944f439..da2340d 100644
--- a/generator/test-js-usage/template.templ
+++ b/generator/test-js-usage/template.templ
@@ -7,7 +7,7 @@ var onceHandle = templ.NewOnceHandle()
 templ TestComponent() {
 	<button onClick={ templ.JSFuncCall("alert", "Hello, World!") }>Click me</button>
 	@onceHandle.Once() {
-		<script type="text/javascript">
+		<script>
 			function customAlert(msg, date) {
 				alert(msg + " " + date);
 			}
diff --git a/generator/test-js-usage/template_templ.go b/generator/test-js-usage/template_templ.go
index fdd7e54..1e450df 100644
--- a/generator/test-js-usage/template_templ.go
+++ b/generator/test-js-usage/template_templ.go
@@ -61,7 +61,7 @@ func TestComponent() templ.Component {
 				}()
 			}
 			ctx = templ.InitializeContext(ctx)
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<script type=\"text/javascript\">\n\t\t\tfunction customAlert(msg, date) {\n\t\t\t\talert(msg + \" \" + date);\n\t\t\t}\n\t\t</script>")
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "<script>\n\t\t\tfunction customAlert(msg, date) {\n\t\t\t\talert(msg + \" \" + date);\n\t\t\t}\n\t\t</script>")
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
diff --git a/generator/test-once/expected.html b/generator/test-once/expected.html
index 2103bd2..84eaf7d 100644
--- a/generator/test-once/expected.html
+++ b/generator/test-once/expected.html
@@ -1,4 +1,4 @@
-<script type="text/javascript">
+<script>
 			function hello(name) {
 				alert('Hello, ' + name + '!');
 			}
diff --git a/generator/test-once/template.templ b/generator/test-once/template.templ
index 7fd03e8..0486047 100644
--- a/generator/test-once/template.templ
+++ b/generator/test-once/template.templ
@@ -4,7 +4,7 @@ var helloHandle = templ.NewOnceHandle()
 
 templ hello(label, name string) {
 	@helloHandle.Once() {
-		<script type="text/javascript">
+		<script>
 			function hello(name) {
 				alert('Hello, ' + name + '!');
 			}
diff --git a/generator/test-once/template_templ.go b/generator/test-once/template_templ.go
index 930687d..780684a 100644
--- a/generator/test-once/template_templ.go
+++ b/generator/test-once/template_templ.go
@@ -42,7 +42,7 @@ func hello(label, name string) templ.Component {
 				}()
 			}
 			ctx = templ.InitializeContext(ctx)
-			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script type=\"text/javascript\">\n\t\t\tfunction hello(name) {\n\t\t\t\talert('Hello, ' + name + '!');\n\t\t\t}\n\t\t</script>")
+			templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<script>\n\t\t\tfunction hello(name) {\n\t\t\t\talert('Hello, ' + name + '!');\n\t\t\t}\n\t\t</script>")
 			if templ_7745c5c3_Err != nil {
 				return templ_7745c5c3_Err
 			}
diff --git a/generator/test-only-scripts/expected.html b/generator/test-only-scripts/expected.html
index 7b6e783..5072ab8 100644
--- a/generator/test-only-scripts/expected.html
+++ b/generator/test-only-scripts/expected.html
@@ -1,7 +1,7 @@
-<script type="text/javascript">
+<script>
   function __templ_withParameters_1056(a, b, c){console.log(a, b, c);
 }
 </script>
-<script type="text/javascript">
+<script>
   __templ_withParameters_1056("hello","world",42069)
 </script>
\ No newline at end of file
diff --git a/generator/test-raw-elements/expected.html b/generator/test-raw-elements/expected.html
index 86b5e3a..5b41f2a 100644
--- a/generator/test-raw-elements/expected.html
+++ b/generator/test-raw-elements/expected.html
@@ -7,7 +7,7 @@
           border: 1px solid black;
         }
     </style>
-		<script type="text/javascript">
+		<script>
         $("div").marquee();
         function test() {
               window.open("https://example.com")
diff --git a/generator/test-raw-elements/template.templ b/generator/test-raw-elements/template.templ
index 782ef14..9073f2c 100644
--- a/generator/test-raw-elements/template.templ
+++ b/generator/test-raw-elements/template.templ
@@ -10,7 +10,7 @@ templ Example() {
           border: 1px solid black;
         }
       </style>
-			<script type="text/javascript">
+			<script>
         $("div").marquee();
         function test() {
               window.open("https://example.com")
diff --git a/generator/test-raw-elements/template_templ.go b/generator/test-raw-elements/template_templ.go
index f91e7db..ab22de8 100644
--- a/generator/test-raw-elements/template_templ.go
+++ b/generator/test-raw-elements/template_templ.go
@@ -28,7 +28,7 @@ func Example() templ.Component {
 			templ_7745c5c3_Var1 = templ.NopComponent
 		}
 		ctx = templ.ClearChildren(ctx)
-		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<html><head></head><body><style><!-- Some stuff --></style><style>\n        .customClass {\n          border: 1px solid black;\n        }\n      </style><script type=\"text/javascript\">\n        $(\"div\").marquee();\n        function test() {\n              window.open(\"https://example.com\")\n        }\n      </script><h1>Hello</h1>")
+		templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<html><head></head><body><style><!-- Some stuff --></style><style>\n        .customClass {\n          border: 1px solid black;\n        }\n      </style><script>\n        $(\"div\").marquee();\n        function test() {\n              window.open(\"https://example.com\")\n        }\n      </script><h1>Hello</h1>")
 		if templ_7745c5c3_Err != nil {
 			return templ_7745c5c3_Err
 		}
diff --git a/generator/test-script-inline/expected.html b/generator/test-script-inline/expected.html
index ce1ac4d..8892088 100644
--- a/generator/test-script-inline/expected.html
+++ b/generator/test-script-inline/expected.html
@@ -1,20 +1,20 @@
-<script type="text/javascript">
+<script>
 	function __templ_withoutParameters_6bbf(){alert("hello");
 }
 </script>
-<script type="text/javascript">
+<script>
   __templ_withoutParameters_6bbf()
 </script>
-<script type="text/javascript">
+<script>
   function __templ_withParameters_1056(a, b, c){console.log(a, b, c);
 }
 </script>
-<script type="text/javascript">
+<script>
   __templ_withParameters_1056("injected","test",123)
 </script>
-<script type="text/javascript">
+<script>
   __templ_withoutParameters_6bbf()
 </script>
-<script type="text/javascript">
+<script>
   __templ_withParameters_1056("injected","test",123)
 </script>
diff --git a/generator/test-script-usage-nonce/expected.html b/generator/test-script-usage-nonce/expected.html
index 9f15abc..b896518 100644
--- a/generator/test-script-usage-nonce/expected.html
+++ b/generator/test-script-usage-nonce/expected.html
@@ -1,4 +1,4 @@
-<script type="text/javascript" nonce="nonce1">
+<script nonce="nonce1">
 	function __templ_withParameters_1056(a, b, c){console.log(a, b, c);
 }function __templ_withoutParameters_6bbf(){alert("hello");
 }
@@ -7,12 +7,12 @@
 <button onClick="__templ_withParameters_1056(&#34;test&#34;,&#34;B&#34;,123)" onMouseover="__templ_withoutParameters_6bbf()" type="button">B</button>
 <button onMouseover="console.log(&#39;mouseover&#39;)" type="button">Button C</button>
 <button hx-on::click="alert('clicked inline')" type="button">Button D</button>
-<script type="text/javascript" nonce="nonce1">
+<script nonce="nonce1">
   function __templ_onClick_657d(){alert("clicked");
 }
 </script>
 <button hx-on::click="__templ_onClick_657d()" type="button">Button E</button>
-<script type="text/javascript" nonce="nonce1">
+<script nonce="nonce1">
   function __templ_conditionalScript_de41(){alert("conditional");
 }
 </script>
diff --git a/generator/test-script-usage/expected.html b/generator/test-script-usage/expected.html
index 798af28..b0dec32 100644
--- a/generator/test-script-usage/expected.html
+++ b/generator/test-script-usage/expected.html
@@ -1,4 +1,4 @@
-<script type="text/javascript">
+<script>
 	function __templ_withParameters_1056(a, b, c){console.log(a, b, c);
 }function __templ_withoutParameters_6bbf(){alert("hello");
 }
@@ -7,22 +7,22 @@
 <button onClick="__templ_withParameters_1056(&#34;test&#34;,&#34;B&#34;,123)" onMouseover="__templ_withoutParameters_6bbf()" type="button">B</button>
 <button onMouseover="console.log(&#39;mouseover&#39;)" type="button">Button C</button>
 <button hx-on::click="alert('clicked inline')" type="button">Button D</button>
-<script type="text/javascript">
+<script>
   function __templ_onClick_657d(){alert("clicked");
 }
 </script>
 <button hx-on::click="__templ_onClick_657d()" type="button">Button E</button>
-<script type="text/javascript">
+<script>
   function __templ_whenButtonIsClicked_253e(event){console.log(event.target)
 }
 </script>
 <button onclick="__templ_whenButtonIsClicked_253e(event)">Button F</button>
-<script type="text/javascript">
+<script>
   function __templ_conditionalScript_de41(){alert("conditional");
 }
 </script>
 <input type="button" value="Click me" onclick="__templ_conditionalScript_de41()" />
-<script type="text/javascript">
+<script>
   function __templ_alertTest_eadf(){alert('testing');
 }
 </script>
diff --git a/handler_test.go b/handler_test.go
index 7e7d6ce..b3f3658 100644
--- a/handler_test.go
+++ b/handler_test.go
@@ -189,8 +189,8 @@ func TestHandler(t *testing.T) {
 		}
 		// Expect the status code to be 201, not 400, because in streaming mode,
 		// we have to write the header before we can call the error handler.
-		if actualRepsonseCode := w.Result().StatusCode; http.StatusCreated != actualRepsonseCode {
-			t.Errorf("expected status %d, got %d", http.StatusCreated, actualRepsonseCode)
+		if actualResponseCode := w.Result().StatusCode; http.StatusCreated != actualResponseCode {
+			t.Errorf("expected status %d, got %d", http.StatusCreated, actualResponseCode)
 		}
 		// Expect the body to be "BodyError message", not just "Error message" because
 		// in streaming mode, we've already written part of the body to the response, unlike in
diff --git a/js_test.go b/js_test.go
index 35cfe8b..e774085 100644
--- a/js_test.go
+++ b/js_test.go
@@ -68,7 +68,7 @@ func TestJSFuncCall(t *testing.T) {
 				Call:       "doSomething()",
 				CallInline: "doSomething()",
 			},
-			expectedComponentOutput: `<script type="text/javascript">doSomething()</script>`,
+			expectedComponentOutput: `<script>doSomething()</script>`,
 		},
 		{
 			name:         "single argument is supported",
@@ -80,7 +80,7 @@ func TestJSFuncCall(t *testing.T) {
 				Call:       "alert(&#34;hello&#34;)",
 				CallInline: `alert("hello")`,
 			},
-			expectedComponentOutput: `<script type="text/javascript">alert("hello")</script>`,
+			expectedComponentOutput: `<script>alert("hello")</script>`,
 		},
 		{
 			name:         "multiple arguments are supported",
@@ -92,7 +92,7 @@ func TestJSFuncCall(t *testing.T) {
 				Call:       "console.log(&#34;hello&#34;,&#34;world&#34;)",
 				CallInline: `console.log("hello","world")`,
 			},
-			expectedComponentOutput: `<script type="text/javascript">console.log("hello","world")</script>`,
+			expectedComponentOutput: `<script>console.log("hello","world")</script>`,
 		},
 		{
 			name:         "attribute injection fails",
@@ -104,7 +104,7 @@ func TestJSFuncCall(t *testing.T) {
 				Call:       "__templ_invalid_js_function_name()",
 				CallInline: "__templ_invalid_js_function_name()",
 			},
-			expectedComponentOutput: `<script type="text/javascript">__templ_invalid_js_function_name()</script>`,
+			expectedComponentOutput: `<script>__templ_invalid_js_function_name()</script>`,
 		},
 		{
 			name:         "closing the script and injecting HTML fails",
@@ -116,7 +116,7 @@ func TestJSFuncCall(t *testing.T) {
 				Call:       "__templ_invalid_js_function_name()",
 				CallInline: "__templ_invalid_js_function_name()",
 			},
-			expectedComponentOutput: `<script type="text/javascript">__templ_invalid_js_function_name()</script>`,
+			expectedComponentOutput: `<script>__templ_invalid_js_function_name()</script>`,
 		},
 	}
 	for _, tt := range tests {
diff --git a/parser/v2/ifexpressionparser_test.go b/parser/v2/ifexpressionparser_test.go
index d80300e..a7ecd6e 100644
--- a/parser/v2/ifexpressionparser_test.go
+++ b/parser/v2/ifexpressionparser_test.go
@@ -622,9 +622,19 @@ func TestIncompleteIf(t *testing.T) {
 	t.Run("no opening brace", func(t *testing.T) {
 		input := parse.NewInput(`if a tree falls in the woods`)
 		_, _, err := ifExpression.Parse(input)
-		if err.Error() != "if: unterminated (missing closing '{\\n') - https://templ.guide/syntax-and-usage/statements#incomplete-statements: line 0, col 0" {
+		if err == nil {
+			t.Fatal("expected an error, got nil")
+		}
+		pe, isParseError := err.(parse.ParseError)
+		if !isParseError {
+			t.Fatalf("expected a parse error, got %T", err)
+		}
+		if pe.Msg != "if: unterminated (missing closing '{\\n') - https://templ.guide/syntax-and-usage/statements#incomplete-statements" {
 			t.Fatalf("unexpected error: %v", err)
 		}
+		if pe.Pos.Line != 0 {
+			t.Fatalf("unexpected line: %d", pe.Pos.Line)
+		}
 	})
 	t.Run("capitalised If", func(t *testing.T) {
 		input := parse.NewInput(`If a tree falls in the woods`)
diff --git a/parser/v2/switchexpressionparser_test.go b/parser/v2/switchexpressionparser_test.go
index e17cae7..376d7ba 100644
--- a/parser/v2/switchexpressionparser_test.go
+++ b/parser/v2/switchexpressionparser_test.go
@@ -322,8 +322,18 @@ func TestIncompleteSwitch(t *testing.T) {
 	t.Run("no opening brace", func(t *testing.T) {
 		input := parse.NewInput(`switch with no brace`)
 		_, _, err := switchExpression.Parse(input)
-		if err.Error() != "switch: unterminated (missing closing '{\\n') - https://templ.guide/syntax-and-usage/statements#incomplete-statements: line 0, col 0" {
-			t.Fatalf("unexpected error: %v", err)
+		if err == nil {
+			t.Fatal("expected an error, got nil")
+		}
+		pe, isParseError := err.(parse.ParseError)
+		if !isParseError {
+			t.Fatalf("expected a parse error, got %T", err)
+		}
+		if pe.Msg != "switch: unterminated (missing closing '{\\n') - https://templ.guide/syntax-and-usage/statements#incomplete-statements" {
+			t.Errorf("unexpected error: %v", err)
+		}
+		if pe.Pos.Line != 0 {
+			t.Errorf("unexpected line: %d", pe.Pos.Line)
 		}
 	})
 	t.Run("capitalised Switch", func(t *testing.T) {
diff --git a/parser/v2/templateparser_test.go b/parser/v2/templateparser_test.go
index 5cf1ee7..c979315 100644
--- a/parser/v2/templateparser_test.go
+++ b/parser/v2/templateparser_test.go
@@ -876,7 +876,7 @@ func TestTemplateParserErrors(t *testing.T) {
 			input: `templ Name(p Parameter) {
 <span
 }`,
-			expected: "<span>: malformed open element: line 2, col 0",
+			expected: "<span>: malformed open element: line 3, col 0",
 		},
 	}
 	for _, tt := range tests {
diff --git a/parser/v2/textparser_test.go b/parser/v2/textparser_test.go
index 2e4602f..d6b25d1 100644
--- a/parser/v2/textparser_test.go
+++ b/parser/v2/textparser_test.go
@@ -80,7 +80,7 @@ func TestTextParser(t *testing.T) {
 			},
 		},
 		{
-			name:  "Multiline text is colected line by line",
+			name:  "Multiline text is collected line by line",
 			input: "Line 1\nLine 2",
 			expected: Text{
 				Value: "Line 1",
@@ -92,7 +92,7 @@ func TestTextParser(t *testing.T) {
 			},
 		},
 		{
-			name:  "Multiline text is colected line by line (Windows)",
+			name:  "Multiline text is collected line by line (Windows)",
 			input: "Line 1\r\nLine 2",
 			expected: Text{
 				Value: "Line 1",
diff --git a/safehtml/style_test.go b/safehtml/style_test.go
index 7617da1..c55a44a 100644
--- a/safehtml/style_test.go
+++ b/safehtml/style_test.go
@@ -172,7 +172,7 @@ func TestSanitizeCSS(t *testing.T) {
 			expectedValue:    InnocuousPropertyValue,
 		},
 		{
-			name:             "background-image invalid sufix",
+			name:             "background-image invalid suffix",
 			inputProperty:    "background-image",
 			expectedProperty: "background-image",
 			inputValue:       `url("/img.png`,
diff --git a/scripttemplate_test.go b/scripttemplate_test.go
index 618843e..b721bd1 100644
--- a/scripttemplate_test.go
+++ b/scripttemplate_test.go
@@ -28,7 +28,7 @@ func TestRenderScriptItems(t *testing.T) {
 			name:     "if none are ignored, everything is rendered",
 			toIgnore: nil,
 			toRender: []templ.ComponentScript{s1, s2},
-			expected: `<script type="text/javascript">` + s1.Function + s2.Function + `</script>`,
+			expected: `<script>` + s1.Function + s2.Function + `</script>`,
 		},
 		{
 			name: "if something outside the expected is ignored, if has no effect",
@@ -39,13 +39,13 @@ func TestRenderScriptItems(t *testing.T) {
 				},
 			},
 			toRender: []templ.ComponentScript{s1, s2},
-			expected: `<script type="text/javascript">` + s1.Function + s2.Function + `</script>`,
+			expected: `<script>` + s1.Function + s2.Function + `</script>`,
 		},
 		{
 			name:     "if one is ignored, it's not rendered",
 			toIgnore: []templ.ComponentScript{s1},
 			toRender: []templ.ComponentScript{s1, s2},
-			expected: `<script type="text/javascript">` + s2.Function + `</script>`,
+			expected: `<script>` + s2.Function + `</script>`,
 		},
 		{
 			name: "if all are ignored, not even style tags are rendered",