-
-
Notifications
You must be signed in to change notification settings - Fork 287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
refactor: update dev / watch mode so that it doesn't modify unchanged _templ.go files #700
Comments
In the meantime, I have added a
#!/bin/bash
check_for_templ_watch_mode() {
# Get the output of 'git diff --cached' command
diff_output=$(git diff --cached)
# Check if the specified string is present in the diff output
if [[ "$diff_output" =~ "templ.WriteWatchModeString" ]]; then
cat <<\EOF
Error: It looks like you're running templ in watch mode.
The string 'templ.WriteWatchModeString' is present in the commit.
Exit templ watch mode to proceed.
EOF
exit 1 # Reject the commit
fi
}
# Call the function to check the diff
check_for_templ_watch_mode
# If the check passes, allow the commit
exit 0 Example usage: $ git commit -m "update template"
Error: It looks like you're running templ in watch mode.
The string 'templ.WriteWatchModeString' is present in the commit.
Exit templ watch mode to proceed. |
Thanks, I've been thinking about this, and I also came up with the That should stop any thrashing of local files (and corresponding git status annoyance) until you exit dev mode, and the compiled files are updated. Should be fairly easy to implement, but would need to check it doesn't harm runtime performance too badly, since it would involve a number of additional code branches. |
If the environment variable were only checked once (perhaps via I would be open to making a PR. Can you point me to areas in the code base that I should look through? I'm unfamiliar with it, so I want to avoid missing a key part of the codebase. |
I don't think that the performance impact would come from the reading of the config. Currently in prod builds the "raw html" bits of the code are generated as string literals, and in the dev mode it reads from a txt file. We will need to create a way for generated files to do both depending on the mode, this'll likely involve some function calls and allocations (maybe). |
An example of how this could work is using the new std lib // generated code
templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(cmp.Or(templ.WatchModeString(1), "<div class=\"border border-gray-100 rounded shadow p-2\">"))
if templ_7745c5c3_Err != nil {
return templ_7745c5c3_Err
} Where |
Given you're generating code, you could avoid a function call in production by adding a (cheap) conditional check based on a global variable. e.g. // somewhere deep inside templ
var DevMode bool
func init() {
DevMode = strings.TrimSpace(os.Getenv("TEMPL_DEV_MODE")) != ""
}
// somewhere in a _templ.go file
func MyView() templ.Component {
...
if (templ.DevMode) {
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
} else {
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("<the production stuff>")
}
} |
From a quick benchmark (https://github.com/a-h/function-vs-variable-vs-if)
Basically, this is the fastest dev mode variant: func Var(w io.Writer, devMode bool) {
s := "prod"
if devMode {
s = "dev"
}
w.Write([]byte(s))
} The cost is roughly an additional 2ns per string write operation. There's no difference on the function call because it likely gets inlined due to the small function size. So there is a genuine (albeit small) cost to the idea of universal generation for dev and prod mode. |
New idea. Get each file to contain a list of strings constants (at the end of each file), each file would create its own variable name for the strings. // Slice behaviour.
var strings = [][]byte{
[]byte("prod"),
}
// Load the values from the dev file.
func UpdateStrings() {
strings[0] = []byte("dev")
}
func Slice(w io.Writer) {
w.Write(strings[0])
} Switching to dev mode is then just a case of loading the string values from disk. The generated files are then only changed if the content changes. Performance is on par with the existing behaviour:
|
That's a good shout. In this design how would a call to UpdateStrings be done, I'm struggling to visualize it? |
Is it okay to vote for removing the "watch" mode here? I don't think this is a responsibility that templ should have and setting it up should just be plumbing a couple of already existing technologies together. I created a little example (https://github.com/bastianwegge/go-templ-reload) we use with a small dev-team on a daily basis and everybody (so far) seems happy with it. This example uses go's native |
@bastianwegge I think there is a place for that if it suits you. But the |
"Sub second" probably isn't doing it justice 😂 |
@joerdav I think I get what the watch mode is supposed to do, but I don't get why this functionality is something that resides within templ. If you're not building a component library, you're probably using a server framework like echo/gin/fiber or native http, I haven't found templ's watch mode particularly useful for that purpose. That's just my experience and I'd love to hear why or how templ watch mode can improve the process here. If the answer is "it's just another way" then I'd argue that we'd be better of providing a simple example and removing the unnecessary JS / Proxy code from templs codebase. Less is more! |
As of now it's not just another way. The watch mode doesn't just watch for changes and recompile, it also stores some of the templates separate to the go binary, so that as you edit they can be updated without recompiling your application, this definitely wouldn't be possible with an external tool, as it involves templ to keep track of these template parts. |
Another thought to add on the pile. One thing we could do to remove the need for But if dev mode was detected it would reference the templ file ranges, if it was prod mode it would use the strings within the template. |
I've put together a draft PR at #1027 - it would be great if folks could try it out. It still uses I've changed the watch behaviour to check whether the project needs to be recompiled or not based on whether Go expressions in the templ file have changed (not just whether the file has changed on disk). |
#1027 works great for me! I can run in watch mode, change a |
I love using dev mode; it's a super productive feature! Thanks for adding it 😄
The feature could be improved by ensuring that dev mode does not cause a dirty working copy for files that have not been modified. I find myself perilously close to accidentally committing modified
<file>_templ.go
files, where the diff is basically:I get why it is written this way, but it would be absolutely wonderful if I could run dev mode, make changes to my
.templ
files, and be able to commit modified_templ.go
files without fear of committing the wrong thing.This would suggest an alternate mechanism for determining whether the go application is in dev mode at runtime. A few ideas of the top of my head:
TEMPL_DEV_MODE
)_templ.txt
files, and automatically load them if they are present (perhaps paired with an environment variable to avoid this work when running a production application)The text was updated successfully, but these errors were encountered: