Skip to content

Commit

Permalink
feat!: improve live reload (#4)
Browse files Browse the repository at this point in the history
Simplifies the code as well as massively improves the debouncing
behavior.

CHANGE: The debounce value now defaults to 100ms instead of 0ms.
BREAKING-CHANGE: The live reload function now sends just the message
`:reload` to the calling process instead of `{:reload, extension}`.
  • Loading branch information
mhanberg authored Oct 11, 2024
1 parent 4b7e4fb commit be0e530
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 41 deletions.
1 change: 0 additions & 1 deletion lib/web_dev_utils/code_reloader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,3 @@ defmodule WebDevUtils.CodeReloader do
Mix.Task.run("compile.elixir")
end
end

53 changes: 13 additions & 40 deletions lib/web_dev_utils/live_reload.ex
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,27 @@ defmodule WebDevUtils.LiveReload do

def reload!({:file_event, _watcher_pid, {path, _event}}, opts \\ []) do
patterns = Keyword.get(opts, :patterns, [])
debounce = Keyword.get(opts, :debounce, 0)
callback = Keyword.get(opts, :callback, &Function.identity/1)
debounce = Keyword.get(opts, :debounce, 100)

if matches_any_pattern?(path, patterns) do
ext = Path.extname(path)
Process.sleep(debounce)

for {path, ext} <- [{path, ext} | debounce(debounce, [ext], patterns)] do
asset_type = remove_leading_dot(ext)
Logger.debug("Live reload: #{Path.relative_to_cwd(path)}")
paths = flush([Path.relative_to_cwd(to_string(path))])

callback.(path)
path = Enum.find(paths, fn path -> Enum.any?(patterns, &String.match?(path, &1)) end)

send(self(), {:reload, asset_type})
end
end
end

defp debounce(0, _exts, _patterns), do: []
if path do
Logger.debug("Live reload: #{Path.relative_to_cwd(path)}")

defp debounce(time, exts, patterns) when is_integer(time) and time > 0 do
Process.send_after(self(), :debounced, time)
debounce(exts, patterns)
send(self(), :reload)
end
end

defp debounce(exts, patterns) do
defp flush(acc) do
receive do
:debounced ->
[]

{:file_event, _pid, {path, _event}} ->
ext = Path.extname(path)

if matches_any_pattern?(path, patterns) and ext not in exts do
[{path, ext} | debounce([ext | exts], patterns)]
else
debounce(exts, patterns)
end
{:file_event, _, {path, _event}} ->
flush([Path.relative_to_cwd(to_string(path)) | acc])
after
0 -> acc
end
end

defp matches_any_pattern?(path, patterns) do
path = to_string(path)

Enum.any?(patterns, fn pattern ->
String.match?(path, pattern) and not String.match?(path, ~r{(^|/)_build/})
end)
end

defp remove_leading_dot("." <> rest), do: rest
defp remove_leading_dot(rest), do: rest
end

0 comments on commit be0e530

Please sign in to comment.