defmodule Mix.Tasks.Texas.Ify do use Mix.Task @shortdoc "Take an existing Phoenix project and Texasify it" @moduledoc """ Adds Texas hooks into the current Phoenix application (assumed to be create with `mix phx.new`). This will modify the following files as described, so it is wise to commit your changes prior to running this command: - `./assets/js/app.js` - (adds `texasjs` dependency and instantiates a new Texas socket) - `./lib/_web/channels/user_socket.ex` - adds `use Texas.Socket` after `use Phoenix.Socket` - `./config/config.exs` - Specifies endpoint and router modules and configures templating engine to compile .texas files - `./lib/_web/router.ex` - Adds the `Texas.Plug` to your browser pipeline - `./lib/_web.ex/` - imports `Texas.Controller` into your app's controller template # - `./lib/_web/controllers/page_controller.ex` - Renders views with `texas_render` # - Renames `index.html.eex` to `index.html.texas` Usage: mix texas.ify """ @impl true def run(_args) do # TODO: Check for Phoenix version? Mix.shell().info("Texasifying this project") app = Mix.Project.config()[:app] app_js = "./assets/js/app.js" [user_socket] = Path.wildcard("./lib/*_web/channels/user_socket.ex") config = "./config/config.exs" [router] = Path.wildcard("./lib/*_web/router.ex") app_name = config |> File.stream!() |> Enum.find(& String.contains?(&1, "Endpoint") && String.contains?(&1, inspect app)) |> String.split(",") |> Enum.at(1) |> String.trim() [web_templates] = Path.wildcard("./lib/#{to_string(app)}_web.ex") Mix.shell().info("- Adding texasjs socket to app.js") # If user hasn't uncommented the socket import in app.js, do it for them replace_in_file(app_js, "// import socket", "import socket", false) insert_into_file(app_js, "import socket from \"./socket\"", "import Texas from \"texasjs\"\nnew Texas(socket)") Mix.shell().info("Adding Texas into the backend socket") insert_into_file(user_socket, "use Phoenix.Socket", " use Texas.Socket") Mix.shell().info("Added Texas stuff to config.exs") insert_into_file(config, "use Mix.Config", """ config :phoenix, :template_engines, texas: Texas.TemplateEngine config :texas, pubsub: #{app_name}.Endpoint config :texas, router: #{app_name}.Router """) Mix.shell().info("Adding Texas to the browser pipeline") insert_into_file(router, " pipeline :browser do", " end", " plug Texas.Plug") Mix.shell().info("Adding Texas to the controller template") insert_into_file(web_templates, "use Phoenix.Controller", "import", " import Texas.Controller") Mix.shell().info("Done!") end defp replace_in_file(path, text, replacement_text, check? \\ true) do input = File.read!(path) unless String.contains?(input, replacement_text) and check? do text = String.replace(input, text, replacement_text) File.write!(path, text) end end defp insert_into_file(path, previous_line, text_to_add) do replace_in_file(path, previous_line, "#{previous_line}\n#{text_to_add}") end defp insert_into_file(path, section_start, section_end, text_to_add) do unless String.contains?(File.read!(path), text_to_add) do f = File.stream!(path) {pre, rest} = Enum.split_while(f, & not String.contains?(&1, section_start)) {section, end_and_beyond} = Enum.split_while(rest, & not String.contains?(&1, section_end)) out = pre ++ [section] ++ [text_to_add] ++ ["\n"] ++ [end_and_beyond] |> Enum.join() File.write!(path, out) end end end