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

Cannot use "watch" with "serve" #805

Closed
David-Else opened this issue Feb 14, 2021 · 14 comments · Fixed by #2816
Closed

Cannot use "watch" with "serve" #805

David-Else opened this issue Feb 14, 2021 · 14 comments · Fixed by #2816

Comments

@David-Else
Copy link

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 #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.

@zaydek
Copy link

zaydek commented Feb 14, 2021

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).

@evanw
Copy link
Owner

evanw commented Feb 14, 2021

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.

@dzearing
Copy link

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"

@zaydek
Copy link

zaydek commented Mar 10, 2021

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.

@dzearing
Copy link

@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.

@dzearing
Copy link

dzearing commented Mar 10, 2021

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.

@zaydek
Copy link

zaydek commented Mar 10, 2021

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.

@dzearing
Copy link

@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!

@avindra
Copy link

avindra commented Mar 10, 2021

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.

@dzearing
Copy link

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. :)

@hum-n
Copy link

hum-n commented Mar 13, 2021

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. ✌️

@chrisdugne
Copy link

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

@assapir
Copy link

assapir commented May 11, 2021

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?

@darpell
Copy link

darpell commented Jul 7, 2022

There is a working solution found here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants