Skip to content
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

Dynamic assets not loading in dev mode since updating vite to v5.0.0+ #3240

Closed
BenoitBotton opened this issue Feb 6, 2024 · 25 comments · Fixed by #3254
Closed

Dynamic assets not loading in dev mode since updating vite to v5.0.0+ #3240

BenoitBotton opened this issue Feb 6, 2024 · 25 comments · Fixed by #3254
Labels
Bug Something isn't working

Comments

@BenoitBotton
Copy link
Contributor

Description

since updating to vite 5.0.0+, dynamic assets are not served anymore when running wails dev, but the index.html file is instead. things are fine once compiled

To Reproduce

in a brand new project, follow the step in the dynamic assets example
whether fetching a non-exiting file or go.mod, the index.html file gets served
image

I have a repo with an example and troubleshooting steps: https://github.com/BenoitBotton/assethandler

Expected behaviour

expect files to get served or an error produced as in the example

Screenshots

No response

Attempted Fixes

No response

System Details

# Wails
Version | v2.7.1

# System
┌─────────────────────────────────────────────────────────────────────────────────────────────────┐
| OS           | Windows 10 Pro                                                                   |
| Version      | 2009 (Build: 22631)                                                              |
| ID           | 23H2                                                                             |
| Go Version   | go1.20.1                                                                         |
| Platform     | windows                                                                          |
| Architecture | amd64                                                                            |
| CPU          | AMD Ryzen 9 5900HX with Radeon Graphics                                          |
| GPU 1        | AMD Radeon(TM) Graphics (Advanced Micro Devices, Inc.) - Driver: 31.0.21023.2010 |
| GPU 2        | NVIDIA GeForce RTX 3070 Laptop GPU (NVIDIA) - Driver: 31.0.15.4633               |
| Memory       | 64GB                                                                             |
└─────────────────────────────────────────────────────────────────────────────────────────────────┘

# Dependencies
┌───────────────────────────────────────────────────────┐
| Dependency | Package Name | Status    | Version       |
| WebView2   | N/A          | Installed | 121.0.2277.98 |
| Nodejs     | N/A          | Installed | 20.10.0       |
| npm        | N/A          | Installed | 9.8.1         |
| *upx       | N/A          | Available |               |
| *nsis      | N/A          | Installed | v3.08         |
└─────────────── * - Optional Dependency ───────────────┘

# Diagnosis
Optional package(s) installation details:
  - upx : Available at https://upx.github.io/

 SUCCESS  Your system is ready for Wails development!

Additional context

No response

@BenoitBotton BenoitBotton added the Bug Something isn't working label Feb 6, 2024
@BenoitBotton
Copy link
Contributor Author

BenoitBotton commented Feb 6, 2024

  • reverting to vite v4.5.2 and vite/plugin-vue v4.6.2 solved the issue

I tried the following while keeping vite 5.0.0+:

I am puzzled as to why the above config changes do not work with vite 5.0.0+

@leaanthony
Copy link
Member

I believe the answer is somewhere in here: vitejs/vite#8452

@leaanthony
Copy link
Member

Setting appType: 'custom' should work

@BenoitBotton
Copy link
Contributor Author

thanks @leaanthony
indeed, it does, to some extent...
with vite.config.ts:

export default defineConfig({
  plugins: [vue()],
  appType: 'custom',
})

I get the expected behaviour in the console:

let response = await fetch('go.mod')
undefined
await response.text()
'module changeme\n\ngo 1.18\n\nrequire github.com/wailsapp/wails/v2 v2.7.1\n\nrequire (\n\tgithub.com/bep/debounce v1.2.1 // indirect\n\tgithub.com/go-ole/go-ole v1.2.6 // indirect\n\tgithub.com/godbus/dbus/v5 v5.1.0 // indirect\n\tgithub.com/google/uuid v1.3.0 // indirect\n\tgithub.com/jchv/go-winloader v0.0.0-20210711035445-715c2860da7e // indirect\n\tgithub.com/labstack/echo/v4 v4.10.2 // indirect\n\tgithub.com/labstack/gommon v0.4.0 // indirect\n\tgithub.com/leaanthony/go-ansi-parser v1.6.0 // indirect\n\tgithub.com/leaanthony/gosod v1.0.3 // indirect\n\tgithub.com/leaanthony/slicer v1.6.0 // indirect\n\tgithub.com/leaanthony/u v1.1.0 // indirect\n\tgithub.com/mattn/go-colorable v0.1.13 // indirect\n\tgithub.com/mattn/go-isatty v0.0.19 // indirect\n\tgithub.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/rivo/uniseg v0.4.4 // indirect\n\tgithub.com/samber/lo v1.38.1 // indirect\n\tgithub.com/tkrajina/go-reflector v0.5.6 // indirect\n\tgithub.com/valyala/bytebufferpool v1.0.0 // indirect\n\tgithub.com/valyala/fasttemplate v1.2.2 // indirect\n\tgithub.com/wailsapp/go-webview2 v1.0.10 // indirect\n\tgithub.com/wailsapp/mimetype v1.4.1 // indirect\n\tgolang.org/x/crypto v0.14.0 // indirect\n\tgolang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect\n\tgolang.org/x/net v0.17.0 // indirect\n\tgolang.org/x/sys v0.13.0 // indirect\n\tgolang.org/x/text v0.13.0 // indirect\n)\n\n// replace github.com/wailsapp/wails/v2 v2.7.1 => C:\\Users\\Benoit\\go\\pkg\\mod\n'
let response = await fetch('I dont exist')
VM43:1 
        
        
        GET http://wails.localhost:34115/I%20dont%20exist 400 (Bad Request)
(anonymous) @ VM43:1
undefined
await response.text()
'Could not load file I dont exist'

But the app (working from the template) looks like:
image

with the console reporting errors:

:34115/favicon.ico:1 
        
        
        Failed to load resource: the server responded with a status of 400 (Bad Request)
wails.localhost/:1 
        
        
        Failed to load resource: the server responded with a status of 400 (Bad Request)

so the app will probably need some changes as well. I will investigate later, other work issues to deal with first

@BenoitBotton
Copy link
Contributor Author

that was when using wails dev
if building the app with wails build -devtools, everything is fine:
image

@BenoitBotton
Copy link
Contributor Author

with appType: 'cutom' set, I notice some errors in the terminal log:

DEB | [DevWebServer] Serving DevServer at http://localhost:34115
Watching (sub)/directory: D:\source\assethandler
DEB | [AssetHandler] Handling request '/' (file='.')
DEB | [AssetHandler] File '.' not found, serving '/' by AssetHandler
DEB | [ExternalAssetHandler] Loading 'http://localhost:5173/'
DEB | [ExternalAssetHandler] 'http://localhost:5173/' returned not found, using AssetHandler
Requesting file: 
DEB | [AssetHandler] Handling request '/favicon.ico' (file='favicon.ico')
DEB | [AssetHandler] File 'favicon.ico' not found, serving '/favicon.ico' by AssetHandler
DEB | [ExternalAssetHandler] Loading 'http://localhost:5173/favicon.ico'
DEB | [ExternalAssetHandler] 'http://localhost:5173/favicon.ico' returned not found, using AssetHandler
Requesting file: favicon.ico
DEB | [AssetHandler] Handling request '/' (file='.')
DEB | [AssetHandler] File '.' not found, serving '/' by AssetHandler
DEB | [ExternalAssetHandler] Loading 'http://localhost:5173/'
DEB | [ExternalAssetHandler] 'http://localhost:5173/' returned not found, using AssetHandler

so is the issue shifted away from vite, to the ExternalAssetHandler?
I admit I am out of my depth as I don't know all that much about serving files.

@leaanthony
Copy link
Member

Where is the request for "." coming from?

@BenoitBotton
Copy link
Contributor Author

BenoitBotton commented Feb 7, 2024 via email

@stffabi
Copy link
Collaborator

stffabi commented Feb 7, 2024

so is the issue shifted away from vite, to the ExternalAssetHandler?

No that is perfectly fine. Because now Vite returns a 404 error, the request is forwarded to the assethandler. The problem is that now Vite does not work anymore for SPA or MPA, due to using appType: 'custom'. So now with custom one needs to add all the Vite middlewares to handle everything on it's own, therefore it newer serves the index.html anymore. That's why you see the 'could not find file' errors. You can check the Vite configuration by directly navigating to http://localhost:5173/ which is Vite it self. If you don't see your frontend there, then Vite is not configured correctly for SPA.

@stffabi
Copy link
Collaborator

stffabi commented Feb 7, 2024

Where is the request for "." coming from?

That's when requesting the '/' and indicates which file would have been loaded from the assets fs.FS, so in this case the current directory, which is .. Because that is a directory, it will inernally load the index.html from the fs.FS.

@leaanthony
Copy link
Member

Does bun have a Dev server/bundler? 😉

@leaanthony
Copy link
Member

leaanthony commented Feb 7, 2024

Spitballing here.... What if you registered a path prefix with your middleware so a matching request isn't forwarded to the external server at all?

@BenoitBotton
Copy link
Contributor Author

so far I have tried a few things(separately):

none worked.
I've spent some time trying to setup Vite middlewares, but I am really out of my depth. I will not be quick solving this

@leaanthony
Copy link
Member

@stffabi - what if we tried the custom handlers first and forwarded to vite only if that failed?

@stffabi
Copy link
Collaborator

stffabi commented Feb 12, 2024

@stffabi - what if we tried the custom handlers first and forwarded to vite only if that failed?

That would unfortunately change the current behaviour completely. Currently the AssetHandler is a fallback for everything not found. If now people just use that handler to serve content for all paths, that would mean nothing would be served from vite anymore.

I will try to take an in-depth look this week, to see what we could do.

@leaanthony
Copy link
Member

leaanthony commented Feb 12, 2024

Understood that it would change the current behaviour and we don't need to address this until v3 as vite 4 still works fine. If we needed to change behaviour, then we could have that as an option and leave the default. I was just speculating that a workflow of request->asset handler->external server might be an option rather than relying on external server 404s.

@stffabi
Copy link
Collaborator

stffabi commented Feb 12, 2024

I personally would leave the behaviour as it is for v2 and for v3 I would completely drop the AssetHandler and just leave the AssetMiddleware option. Since that is handled in the request chain before going to the assets fs.FS or going to the external dev server.
Only having the AssetMiddleware is also what people are used to when coming from a Web-Framework like standard go http package or gin/echo and so on.
AssetHandler was the first step in v2 at a time when we didn't have an almost full blown http.Server mock.

@leaanthony
Copy link
Member

Love it! Sounds like a plan!

@leaanthony
Copy link
Member

@BenoitBotton I guess the answer for this ticket is that unless you can find a way to make vite 5 work with its new behaviour and defaults, we recommend staying with Vite 4.

@BenoitBotton
Copy link
Contributor Author

BenoitBotton commented Feb 13, 2024 via email

@stffabi
Copy link
Collaborator

stffabi commented Feb 14, 2024

Love it! Sounds like a plan!

After taking a look into v3, I think we should keep the AssetHandler but change the logic to use it just for non GET-requests.

  • GET requests are always served from the fs.FS or the external DevServer.
  • AssetHandler is used for all non GET requests
  • If the fs.FS is nil and no external DevServer is used, all requests are being handled by the AssetHandler

This allows people that don't want to use the fs.FS to serve their content completely from an http.Handler. This is interesting for people that use templ.

@leaanthony thoughts?

@leaanthony
Copy link
Member

leaanthony commented Feb 14, 2024

Does that mean if you want to serve up, say, an image, you have to use POST? Could we not check the path and if it starts with /wails/assethandler/ we direct it there instead?

@stffabi
Copy link
Collaborator

stffabi commented Feb 14, 2024

Does that mean if you want to serve up, say, an image, you have to use POST? Could we not check the path and if it starts with /wails/assethandler/ we direct it there instead?

One can do that with the Middleware if someone wants that. I personally would not introduce another preregistered path for this, because that can be achieved with just a custom middleware. We would just introduce another "magic" thing that is specific to wails.

We can still drop AssetHandler if you want, but then people that want to use templ must add a middleware and drop the next handler. This assumes the middleware is behind all the internal wails middlewares, like the runtime, but we already discussed to lift the middleware up, so it is being called before any wails middlewares and people can add CORS handling.

I'm open to whatever you want. I personally would not introduce some magic paths like /wails/assethandler/ for assethandler, because IMHO that's the use case for a middleware and people are used to this from web frameworks. I think we should strive for a clean solution with clean rules and try to be more like web frameworks.

My preferred solution would be probably like this: Let's just drop the fs.FS completely from the application.AssetOptions and leave http.Handler and middleware. And middleware injects it at the beginning of all requests, even before Wails internal middlewares, so people can add CORS handling or add logging to Wails runtime requests and so on.

Using an fs.FS would then look like this and if someone uses the external devserver, the application.AssetFileServerFS would use the external server instead of using the assets.

app := application.New(application.Options{
	Assets: application.AssetOptions{
		Handler: application.AssetFileServerFS(assets),
	},

And if someone wants to have a custom path for images that get dynamically served, this would look like the following with e.g. chi:

r := chi.NewRouter()
r.Get("/images/*", func(w http.ResponseWriter, r *http.Request) {
	fmt.Println("My Images dynamically served")
	w.WriteHeader(http.StatusOK)
})
r.NotFound(application.AssetFileServerFS(assets).ServeHTTP)

app := application.New(application.Options{
	Assets: application.AssetOptions{
		Handler: r,
	},

Example with http.ServeMux in Go 1.22

mux := &http.ServeMux{}
mux.Handle("/", application.AssetFileServerFS(assets))
mux.HandleFunc("/images/", func(w http.ResponseWriter, r *http.Request) {
	fmt.Println("My Images dynamically served")
	w.WriteHeader(http.StatusOK)
})

app := application.New(application.Options{
	Assets: application.AssetOptions{
		Handler: mux,
	},

@stffabi
Copy link
Collaborator

stffabi commented Feb 16, 2024

Just pushed the above proposal to v3-alpha branch.

@stffabi
Copy link
Collaborator

stffabi commented Feb 22, 2024

We can see this issue as closed. There's currently nothing we can do regarding Vite v5+ and for Wails v3 we have changed the implementation so this is not a problem anymore.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants