esbuild
esbuild copied to clipboard
Cannot use "watch" with "serve"
Hi, I am trying to replace live-server
for my development with the new --servedir=
but having problems getting it to watch the files and serve them. Currently, I have:
#!/bin/bash
npx esbuild --watch --bundle --minify --sourcemap src/mod.ts --outfile=dist/bundle.js &
npx live-server ./dist &
wait
As soon as I save then the browser reloads the file. I see https://github.com/evanw/esbuild/issues/802 , but I don't see the use for --servedir=
unless it refreshes when the files changes somehow?
Can I now replace live-server
with esbuild or am I confused about the new feature? Cheers.
Short answer: Maybe soon but not yet (to the best of my knowledge).
I believe you’re right that this is not yet implemented. watch
has to do with esbuild watching your source files for changes and rebuilding, and servedir
has to do with serving your build directory in a more performant manner (from memory rather than from disk).
servedir
is useful because it saves you the pain of using a Python / Node / Go web server, and is faster even, but does not yet emit events. FWIW servedir
was only added like 24 hours ago or something. It’s conceivable it will be upgraded in the future to emit events, but that’s also not a stated goal of esbuild.
That all being said, this can be implemented using the Node or Go APIs by adding a callback to onRebuild
:
// ...
watch: {
onRebuild(error, result) {
if (error) console.error('watch build failed:', error)
else console.error('watch build succeeded:', result)
},
},
// ...
https://esbuild.github.io/api/#watch
The basic idea is that when esbuild rebuilds (because your source changed), you send a signal to the browser via server sent events / WebSockets (this is the motivation behind #802, to make this a managed experience by esbuild -- as you noted).
Can I now replace
live-server
with esbuild or am I confused about the new feature? Cheers.
No, you can't use this feature to replace live-server
. I would keep using live-server
.
The --servedir=
feature is not an exact replacement because it's just a normal web server. You can potentially build a live reloading feature yourself as described above, but it's probably easiest to just keep using live-server
. I haven't added a form of live reloading because it can get extremely custom (there are many different potentially incompatible ways that people prefer to do it). Instead I am focusing on making useful primitives that can be easily composed with existing tools.
Just to be clear, I'm interpreting that the only way to add live reloading (for now) is to avoid .serve()
altogether and only use watch
mode with live-server
hosting the results.
I would love to use esbuild.serve(...)
for the in memory perf benefits, but there doesn't seem to be any way to provide an onRebuild
callback in options to trigger the live reloading, since the watch
options can't be provided in serve mode. Is there another way to get an onRebuild
callback, or a plan to make this possible?
For context I see this when trying to just hook up the onRebuild
callback in a .serve(...)
call:
(node:93344) UnhandledPromiseRejectionWarning: Error: Cannot use "watch" with "serve"
Yeah I was a little surprised that you can’t use them together. But I don’t know if that was an intentional decision or is simply unimplemented. Insight would be appreciated here.
Anyway, @dzearing, you can just use both, right? Start a watch server and then also start a serve server. If you need to get the browser to update in response to watch events, that’s probably just a matter of hooking up server-sent events or websockets. In my experience I’ve found implementing SSE (server-sent events) to be pretty simple and fun. So doing so you’d get the in-memory perf you’re looking for.
I’ve found esbuild’s API to actually be pretty loosely coupled and therefore composable. It can take a little getting used to but it turns out to be a very powerful API because you can shape esbuild into the kind of tool you need pretty easily.
@zaydek Oh! I totally forgot there's a config setting to use in-memory building - so yeah, I could probably use watch mode, then have express serve up the script from in memory results, and onRebuild
would use WS or SSE to trigger the update.
I'll spin that up and see how it goes.
Hmm. I am using write: false
to avoid writing content to disk, but there doesn't seem to be way to access the in-memory output (result.outputFiles
) in watch mode. Seems like buildSync
is the only way to access in-memory results, so I believe I can't use watch mode either and would have to fall back to chokidar
to monitor source, kick off buildSync
, serve the result, and also do the live reloading work to notify a refresh is needed.
If there's a way to access the watch mode in-memory build results from the onRebuild
callback, that would help. I can't find any information on how to do this in the docs, other than the mention of buildSync
output in the write
section.
And if there's a workaround to getting the onRebuild
callback in serve, that would let me use serve instead of self hosting.
Ah. That kind of makes sense. But why not just write to disk then?
Yeah, I think I know what you mean about mixing callbacks together. I think my strategy so far has been to create an uninitialized function and then initialize it lazily. This kind of thing in Go is a bit easier because you can orchestrate w/ channels but in JavaScript it’s a little awkward unless you know exactly what you’re doing.
@zaydek Mainly I'd love to minimize disk writes and make the inner loop yarn start
experience as fast as possible. I could certainly write to disk, maybe that could even be desirable just to see what's produced, but it costs time and resources that we could probably work around using buildSync
.
It does feel kind of broken to have write: false
option without having a way to access the output in any mode other than buildSync
results. I expected onRebuild
watch callback returning outputFiles
in the result 2nd argument, but it's missing. I don't know Go well enough to make a PR here, or identify if there's any other way to access the output.
@evanw would you consider it worth fixing to expose outputFiles
in onRebuild
args, and/or allow watch onRebuild
callback to somehow be provided when using serve so that we can provide live reloading externally to esbuild?
Thanks for the help!
I'm not sure if this is the intended behavior, but....
If I use --servedir
by itself, the watcher is enabled by default.
So, if you want --watch
, just pass --servedir
alone.
Nevermind on the above outputFiles
comment - it does look like outputFiles
is showing up in the result. I think it was my bad and I didn't save the write:false
in my config. I double checked and now they're showing up. :)
Hey @David-Else, I was using live-server
too but it's super outdated and it includes chokidar
. I built a simple esbuild wrapper that uses esbuild
's watch
and a light http server with live reload. You just pass your esbuild config to it. Here it is if you are interested: esbuild-serve. ✌️
for those willing to use esbuild
+ live-server
I've been copy pasting my setup with live reload along many repos, so I've created a package to share the setup:
https://github.com/uralys/reactor
I'm not sure if this is the intended behavior, but....
If I use
--servedir
by itself, the watcher is enabled by default.So, if you want
--watch
, just pass--servedir
alone.
It might be that it works only from cli, and not from node?
There is a working solution found here