A proof-of-concept integration of Vite.js (modern JS/assets bundler) with Phoenix + Liveview

I have an important update on this solution: It seems that there was some sort of regression and the provided solution to prevent zombie processes doesn’t work anymore (details here and here). If you are still having problems where Vite is leaving orphan processes behind, try changing your setup to use a wrapper script instead. The main idea is explained in the docs here: Port — Elixir v1.12.0-rc.0.

# mix.exs alias for asset deploy... 
# If your deploy process uses docker you can simply use npx here.
# However, if your deploy machine state is persistent, call the binary from the node_modules directly instead.
"assets.deploy": ["cmd --cd assets npx vite build", "phx.digest"]
# dev.exs config watchers
# Call the Vite binary directly in development, which is the most common use-case.
# It's important to call the binary directly because the wrapper will terminate only the direct children.
# phoenix [server] -> node -> esbuild [vite] (works properly when terminated)
# If we call 'npx' here, another node process will be spawned and the process tree will become something like:
# phoenix [server] -> node -> node [npx] -> esbuild [vite] (won't be terminated properly)
# To understand it better, you can observe this problem by echoing the PID in the wrapper script and checking which process in the tree is actually being terminated.
watchers: [
    "#{Path.expand("../assets/wrapper.sh", __DIR__)}": [
      "node_modules/.bin/vite",
      "build",
      "--watch",
      "--minify",
      "false",
      "--emptyOutDir",
      "false",
      "--clearScreen",
      "false",
      "--mode",
      "development",
      cd: Path.expand("../assets", __DIR__)
    ]

wrapper.sh

#!/usr/bin/env bash

# Start the program in the background
exec "$@" &
pid1=$!

# Silence warnings from here on
exec >/dev/null 2>&1

# Read from stdin in the background and
# kill running program when stdin closes
exec 0<&0 $(
  while read; do :; done
  kill -KILL $pid1
) &
pid2=$!

# Clean up
wait $pid1
ret=$?
kill -KILL $pid2
exit $ret
2 Likes