Packages

A library for easily integrating Svelte components into Phoenix

Current section

Files

Jump to
svonix lib mix tasks setup.ex
Raw

lib/mix/tasks/setup.ex

defmodule Mix.Tasks.Svonix.Setup do
@config_file "config/dev.exs"
@js_file "assets/js/app.js"
@esbuild_regex ~r/esbuild: {.*}/
@watcher_regex ~r/watchers:\s\[/
def run(_) do
deps_paths = Mix.Project.deps_paths(depth: 1)
path_to_svonix = Map.fetch!(deps_paths, :svonix)
path_to_svonix_assets = Path.join(path_to_svonix, "assets/*")
# Copy all files in Svonix assets directory to parent app ('assets/svonix_file').
copy_files(path_to_svonix_assets, "assets/")
Mix.Generator.create_directory("assets/js/svelte")
# Inject watcher into config/dev.exs
inject_watcher(@config_file)
# Inject js svonix import into app.js
inject_import(@js_file)
# Run npm install (use :os.cmd for simplicity)
IO.puts "Running npm install. This might take a second."
npm_result = "cd assets && npm install" |> String.to_charlist() |> :os.cmd
IO.puts npm_result
end
defp copy_files(from, into) do
Path.wildcard(from)
|> Enum.each(fn file ->
Mix.Generator.copy_file(file, into <> Path.basename(file))
end)
end
defp inject_import(js_path) do
import_stmt = ~s"""
import 'svonix'
"""
File.read!(js_path)
|> concat_before(import_stmt)
|> save_into(js_path)
Mix.shell().info([:green, "Added 'svonix' import ", :reset, @js_file])
end
@spec concat_before(String.t, String.t) :: String.t
defp concat_before(source, to_concat), do: to_concat <> source
defp inject_watcher(config_path) do
watcher = ~s"""
\n\t# Watcher for sveltex files
\tnode: ["build.js", "--watch", cd: Path.expand("../assets", __DIR__)],
"""
File.read!(config_path)
|> insert_after(@watcher_regex, watcher)
|> inform_status
|> comment_out(@esbuild_regex)
|> inform_status
|> save_into(config_path)
end
defp save_into(source, target_file), do: File.write!(target_file, source)
@spec insert_after(String.t, Regex.t, String.t) :: {:insert_ok, String.t} | {:insert_error, String.t}
def insert_after(source, regex, to_insert) do
case Regex.run(regex, source, return: :index) do
[{pos, len}] -> {:insert_ok, insert_after_position(source, pos + len, to_insert)}
nil -> {:insert_error, source}
end
end
@spec comment_out(String.t, Regex.t) :: String.t | :error
def comment_out(source, regex) do
case Regex.run(regex, source, return: :index) do
[{pos, _len}] -> {:comment_ok, comment_out_at_position(source, pos)}
nil -> {:comment_error, source}
end
end
@spec insert_after_position(String.t, integer, String.t) :: String.t
def insert_after_position(source, position, to_insert) do
{head, tail} = String.split_at(source, position)
head <> to_insert <> tail
end
@spec comment_out_at_position(String.t, integer) :: String.t
def comment_out_at_position(source, position) do
insert_after_position(source, position, "# ")
end
defp inform_status({:insert_error, source}) do
Mix.shell().info([:red, "Could not insert watcher, please do so yourself ", :reset, @config_file])
source
end
defp inform_status({:insert_ok, source}) do
Mix.shell().info([:green, "Inserted watcher in config ", :reset, @config_file])
source
end
defp inform_status({:comment_ok, source}) do
Mix.shell().info([:green, "Commented out esbuild watcher ", :reset, @config_file])
source
end
defp inform_status({:comment_error, source}) do
Mix.shell().info([:yellow, "Could not comment out esbuild watcher ", :reset, @config_file])
source
end
end