defmodule Webpack.Assets do
use GenServer
@moduledoc """
Handles an external (Webpack) pipeline.
Runs the pipeline, then loads the manifest it produces into a GenServer
in order to provide asset name mapping.
"""
@webpack_config_path_default "webpack.config.js"
def start_link(_args) do
GenServer.start_link(__MODULE__, %{}, name: :assets)
end
def init(args) do
{:ok, args}
end
def build() do
if File.exists?(webpack_config_path()) do
System.cmd("yarn", ["run", "webpack"]) |> handle_build_result()
else
{:ok}
end
end
def load_manifest() do
manifest = "build/manifest.json"
|> File.read!
|> Jason.decode!
|> Enum.into(
# Ensure asset names have no initial '/', while
# asset paths do
%{},
fn
{"/" <> k, "/" <> v} -> {k, "/" <> v}
{"/" <> k, v} -> {k, "/" <> v}
{ k, "/" <> v} -> {k, "/" <> v}
{ k, v} -> {k, "/" <> v}
end
)
GenServer.call(:assets, {:put, manifest})
{:ok}
end
def webpack_config_path do
Application.get_env(
:fermo,
:webpack_config_path,
@webpack_config_path_default
)
end
defp handle_build_result({_output, 0}) do
load_manifest()
end
defp handle_build_result({output, _exit_status}) do
{:error, "External webpack pipeline failed to build\n\n#{output}"}
end
def manifest do
GenServer.call(:assets, {:manifest})
end
def path("/" <> name) do
GenServer.call(:assets, {:path, name})
end
def path(name) do
GenServer.call(:assets, {:path, name})
end
def path!(name) do
{:ok, path} = path(name)
path
end
def handle_call({:put, state}, _from, _state) do
{:reply, {:ok}, state}
end
def handle_call({:manifest}, _from, state) do
{:reply, {:ok, state}, state}
end
def handle_call({:path, name}, _from, state) do
if Map.has_key?(state, name) do
path = state[name]
{:reply, {:ok, path}, state}
else
{:reply, {:error, "'#{name}' not found in manifest"}, state}
end
end
end