Running Webpack from Mix

I have a Mix task that I’ve built for running Webpack alongside my Plug server to build the static assets. The task looks like:

defmodule Mix.Tasks.Server do
  use Mix.Task
  require Logger

  @watchers webpack: [bin: "/usr/local/bin/yarn",
                      opts: ["start"]]

  @shortdoc "Starts applications and their servers"
  @moduledoc """
  Starts the application

  ## Command line options

  This task accepts the same command-line arguments as `run`.
  For additional inforamtion, refer to the documentation for

  The `--no-halt` flag is automatically added.
  def run(args) do
    if MyApplication.watch_assets?() , do: spawn(&start_watcher/0) "run", run_args() ++ args

  defp start_watcher do
    validate_watcher() "Starting asset watcher"

    System.cmd watcher_bin(),

  defp validate_watcher do
    unless watcher_exists?() do
      Logger.error "Could not start watcher because #{watcher_bin()} could " <>
                   "not be found. Your dashboard server is running, but " <>
                   "assets won't be compiled."


  defp watcher_exists?, do: File.exists? watcher_bin()

  defp watcher, do: @watchers[:webpack]
  defp watcher_bin, do: watcher()[:bin] |> Path.expand

  defp run_args, do: if iex_running?(), do: [], else: ["--no-halt"]
  defp iex_running?, do: Code.ensure_loaded?(IEx) && IEx.started?

When I quit the server run (with Ctrl-C x 2) the Yarn process is left running. I’ve tried using spawn_link instead of spawn which has no effect. How do I ensure that the yarn start process is stopped with the application?

When you start that yarn stuff directly, does it shut down when you close stdin? If not, you need to create a wrapper script which starts yarn, remembers it’s (OS) PID and kills that PID when it receives EOF on stdin.

Somewhere there was a generic shell-wrapper for such situations, but I can’t find it right now.

I’m not aware of any solution to this problem on windows except using a POSIX-wrapper.

What @NobbZ said yep. If yarn is not closing then it’s not using the unix style method of either forking to persist, or closing when stdin closes, that sounds like bad programming (which I’m irritatingly getting used to from the horrid javascript ecosystem…)…

And yep, I made it, somewhere on these forums (I should probably put it on my blog or something), let me see if I can find it in my history…

Found it: External processes with environment variables

It is entirely stupid to use it if the program is programmed well, but you know how many are, especially in the nonsensical javascript world… ^.^

My project MBU contains such a wrapper:

You may be able to tweak it to your needs. It is run with elixir apprunner.exs command-to-run. It listens to its STDIN to determine if the parent BEAM was closed. If so, it uses kill to terminate the child process. Child process output is sent back to the parent BEAM. If the child process stops on its own, the wrapper also stops.

1 Like

Webpack recently added an --watch-stdin option. Maybe that’s what your npm script is in need of?

1 Like

Adding --watch-stdin fixed it. As to why this isn’t a default option, no one will ever know.

Because they fear the complains of other people that try to start without stdin in the background to replace demonized behaviour.

At least thats my initial guess.

Why not just follow the unix style… >.>

Because its to late now… You know how hard it is to introduce changes that break existing software. Its often not even done when a major jump is done, just to scare no one away…

1 Like

I mean from the get-go, they were stupid not to in my not-so-humble opinion. ^.^;

Because you need to know about idioms to follow them. and not much in the JS world (mostly comming from the browser) did knew about them.

Even on my university it was not even spoken about this idiom, neither in the programming classes nor in the unix/linux classes.

The only reason I do know about this POSIX idiom is because of erlangs ports…