Current section
Files
Jump to
Current section
Files
lib/cli.ex
defmodule Mix.Tasks.Phx.Reactify do
@moduledoc """
Generates directory and dependencies structure to use React inside your
Phoenix project
"""
@shortdoc """
Reactify your Phoenix Project
"""
use Mix.Task
alias PhoenixReactify.Helpers
def run(args) do
options = [
switches: [typescript: :boolean, verbose: :boolean, project_name: :string],
aliases: [t: :typescript, v: :verbose, p: :project_name]
]
{opts, _, _} = OptionParser.parse(args, options)
opts
|> verify_prerequisites
|> verify_if_is_phoenix_project
|> generate_react_project
|> add_remount
IO.inspect(opts, label: "Command Line Arguments")
end
defp verify_prerequisites(opts) do
CliSpinners.spin_fun(
[text: "Verifying Prerequisites", done: "✅ Prerequisites Verified"],
fn ->
tasks = [
Task.async(fn -> Helpers.Npm.available?() end),
Task.async(fn -> Helpers.CreateReactApp.available?() end),
Task.async(fn -> Helpers.Elixir.available?() end),
Task.async(fn -> Helpers.Erlang.available?() end)
]
[
{_, {:ok, npm_version}},
{_, {:ok, create_react_app_version}},
{_, {:ok, elixir_version}},
{_, {:ok, erlang_version}}
] = Task.yield_many(tasks, :infinity)
if opts[:verbose] do
IO.puts("""
NPM: #{npm_version}
CREATE-REACT-APP: #{create_react_app_version}
ELIXIR: #{elixir_version}
ERLANG: #{erlang_version}
""")
end
end
)
opts
end
@doc """
Tries to verify if the current directory is a phoenix project.
- Not 100% effective
- Just as a preventive step.
- Assets mainly by the directory structure.
"""
defp verify_if_is_phoenix_project(opts) do
{:ok, current_path} = File.cwd()
CliSpinners.spin_fun(
[text: "Verifying Phoenix Project", done: "✅ Phoenix Project Verified"],
fn ->
tasks = [
Task.async(fn -> File.exists?("#{current_path}/lib") end),
Task.async(fn -> File.exists?("#{current_path}/deps") end),
Task.async(fn -> File.exists?("#{current_path}/config") end),
Task.async(fn -> File.exists?("#{current_path}/assets") end),
Task.async(fn -> File.exists?("#{current_path}/priv") end),
Task.async(fn -> File.exists?("#{current_path}/test") end)
]
results = Task.yield_many(tasks, :infinity)
Enum.each(results, fn task ->
{_task, {:ok, exists}} = task
if !exists do
raise(RuntimeError, "Is not a Phoenix Project")
end
end)
end
)
opts
end
defp generate_react_project(opts) do
{:ok, current_path} = File.cwd()
File.cd("#{current_path}/assets/js")
{:ok, current_path} = File.cwd()
project_name = if opts[:project_name], do: opts[:project_name], else: "spa"
File.mkdir("#{current_path}/#{project_name}")
File.cd("#{current_path}/assets")
output = PhoenixReactify.Helpers.Npm.install_react(project_name, opts)
if opts[:verbose] do
IO.puts("#{output}")
end
output = PhoenixReactify.Helpers.Npm.add_sample_files(project_name, opts)
if opts[:verbose] do
IO.puts("#{output}")
end
opts
end
defp add_remount(opts) do
{:ok, current_path} = File.cwd()
project_name = if opts[:project_name], do: opts[:project_name], else: "spa"
File.cd!("#{current_path}/#{project_name}")
PhoenixReactify.Helpers.Npm.install_remount(opts)
PhoenixReactify.Helpers.Npm.install_babel_presets(opts)
File.cd!("#{current_path}/#{project_name}/src")
File.touch!("#{current_path}/#{project_name}/src/entrypoint.js")
File.write!("#{current_path}/#{project_name}/src/entrypoint.js", """
import {define} from 'remount';
import App from './App';
define({ 'x-#{project_name}': App });
""")
decoded = Jason.decode!(File.read!("#{current_path}/../.babelrc"))
updatedBabelRc =
Map.put(decoded, "presets", Map.get(decoded, "presets", []) ++ ["@babel/preset-react"])
File.write!(
"#{current_path}/../.babelrc",
"""
#{Jason.encode!(updatedBabelRc)}
"""
)
File.write!(
"#{current_path}/app.js",
"""
import './#{project_name}/src/entrypoint'
""",
[:append]
)
IO.inspect(current_path)
opts
end
end