Laravel Mix is one of my favourite open source projects out there. It has all the power of webpack (and lets you customise it if you wish) but wires up sensible defaults for almost every app that means you rarely have to dig into the config.

Laravel itself is amazing, but I have found myself working in different, non-PHP backend stacks and wanted to use Laravel Mix. Unfortunately the mix() helper is only available within Laravel itself, so I have had to manually port these before.

The process of porting the mix helper is straightforward but takes time to understand the whole context if you're digging into the code directly.

How Laravel Mix and the helper work

Laravel Mix offers hot reloading out of the box by simply running npm run hot. The mix helper function returns a URL to you that takes into account whether you are running hot reload or have built your assets for production.

When you're running npm run hot, a hot file is created in your public directory. If you build your production assets, any hot file is deleted by Laravel Mix & a mix-manifest.json file is created.

When you use the mix helper, it returns a URL to your js file. E.g.

$url = mix('js/app.js');
// in production => $url == '//js/app.js?id=1234567'
// in hot reload mode => $url == 'localhost:8080/js/app.js'

How to implement the Mix helper in other languages

Given that all languages are different, I can't spell out the exact code. That said, the following pseudo code should help with what features and logic you need to implement.

let cache = {};

function mix(path, manifest_directory = '') {
    # ensure the path has a prefix of a single forward slash '/'

    # check if a hot file exists in the manifest_directory
        # if it exists, trim the whitespace off the contents of that file and use that as the URL
            # clean up the URL by removing the scheme. (chop any prefix of https:// and http:// off the string)
            # return url
    # otherwise read the mix-manifest.json file in the manifest_directory
        # parse the contents into json
        # cache the manifest file so you don't have to access the filesystem on every call
            # cache[path] = json
        # url to asset = json[path]
        # return url
}

In elixir-lang this might look like:

def mix(path, manifest_dir) do
  manifest_path = "#{String.replace_suffix(manifest_dir, "/", "")}/mix-manifest.json"
  hot_file_path = "#{String.replace_suffix(manifest_dir, "/", "")}/hot"
  path = String.pad_leading(path, 1, "/")

  case File.read(hot_file_path) do
    {:ok, body} ->
      url = body
        |> String.replace_leading("https://", "")
        |> String.replace_leading("http://", "")

      "//#{String.trim(url)}#{path}"
        {:error, _} ->
      manifest_file = File.read!(manifest_path)
      manifest = Poison.parse!(manifest_file)

      manifest[path]
  end
end

def mix(path) do
  manifest_dir = Path.expand("./", __DIR__)

  mix(path, manifest_dir)
end

def mix_manifest(path) do
  manifest = File.read!(path)
    Poison.decode!(manifest)
end

For an example of an implementation in crystal-lang see randomstate/mix-helper-cr

Hope it helps 💪