Packages

Phoenix Reactify is an open-source tool meant to add a simple React implementation to fresh Phoenix Applications.

Current section

Files

Jump to
phoenix_reactify lib phoenix_reactify.ex
Raw

lib/phoenix_reactify.ex

defmodule PhoenixReactify do
@moduledoc """
Generates directory and dependencies structure to use React inside your
Phoenix project
"""
alias PhoenixReactify.Helpers
def main(args) do
IO.inspect(__ENV__)
options = [
switches: [typescript: :boolean, verbose: :boolean, project_name: :string],
aliases: [t: :typescript, v: :verbose, p: :project_name]
]
{parsed_opts, _, _} = OptionParser.parse(args, options)
assigns =
if !parsed_opts[:project_name],
do: [project_name: "spa", base_path: File.cwd!()],
else: [base_path: File.cwd!()]
opts = parsed_opts ++ assigns
opts
|> verify_prerequisites
|> verify_if_is_phoenix_project
|> generate_react_project
|> add_remount
|> add_tag_to_index_template
|> run_last_check
if opts[:verbose] do
IO.inspect(opts, label: "Command Line Arguments")
end
end
defp verify_prerequisites(opts) do
IO.write("Verifying Prerequisites...")
tasks = [
Task.async(fn -> Helpers.Npm.available?() end),
Task.async(fn -> Helpers.Elixir.available?() end),
Task.async(fn -> Helpers.Erlang.available?() end),
Task.async(fn -> Helpers.Phoenix.available?() end)
]
Task.yield_many(tasks, :infinity)
|> Enum.map(fn {_, {:ok, task}} ->
case task do
{:ok, version, descriptor} ->
if opts[:verbose] do
IO.puts("\n#{String.upcase(descriptor)} installed [#{version}]")
end
{:error, :uncompatible, descriptor} ->
IO.puts("\n#{String.upcase(descriptor)} is not compatible")
IO.puts(
"\nCompatible versions include #{String.upcase(descriptor)} -> #{PhoenixReactify.Versions.get!(String.to_atom(String.downcase(descriptor)))}"
)
IO.puts("\n❌ Pre-Requisites not satisfied")
System.halt(0)
{:error, :not_installed, descriptor} ->
IO.puts("\n#{String.upcase(descriptor)} is not installed")
IO.puts("\n❌ Pre-Requisites not satisfied")
System.halt(0)
end
end)
IO.write("\r✅ Prerequisites Verified\n")
opts
end
# 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
IO.write("Verifying Phoenix Project...")
tasks = [
Task.async(fn -> File.exists?("#{opts[:base_path]}/lib") end),
Task.async(fn -> File.exists?("#{opts[:base_path]}/deps") end),
Task.async(fn -> File.exists?("#{opts[:base_path]}/config") end),
Task.async(fn -> File.exists?("#{opts[:base_path]}/assets") end),
Task.async(fn -> File.exists?("#{opts[:base_path]}/priv") end),
Task.async(fn -> File.exists?("#{opts[:base_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)
IO.write("\r✅ Phoenix Project Verified\n")
opts
end
defp generate_react_project(opts) do
IO.write("Intalling React...")
Helpers.Npm.install_react!(opts)
IO.write("\r✅ React installed\n")
if opts[:typescript] do
IO.write("Intalling TypeScript...")
Helpers.Npm.install_typescript!(opts)
IO.write("\r✅ Typescript installed\n")
end
IO.write("Adding sample files...")
Helpers.Npm.add_sample_files!(opts)
IO.write("\r✅ Sample Files added\n")
opts
end
defp add_remount(opts) do
IO.write("Installing Remount...")
Helpers.Npm.install_remount!(opts)
IO.write("\r✅ Remount installed\n")
IO.write("Adding entrypoints...")
if opts[:typescript] do
File.touch!("#{opts[:base_path]}/assets/js/#{opts[:project_name]}/src/entrypoint.ts")
File.write!(
"#{opts[:base_path]}/assets/js/#{opts[:project_name]}/src/entrypoint.ts",
"""
import {define} from 'remount';
import App from './App';
define({ 'x-#{opts[:project_name]}': App });
"""
)
else
File.touch!("#{opts[:base_path]}/assets/js/#{opts[:project_name]}/src/entrypoint.js")
File.write!(
"#{opts[:base_path]}/assets/js/#{opts[:project_name]}/src/entrypoint.js",
"""
import {define} from 'remount';
import App from './App';
define({ 'x-#{opts[:project_name]}': App });
"""
)
end
File.write!(
"#{opts[:base_path]}/assets/js/app.js",
"""
import './#{opts[:project_name]}/src/entrypoint'
""",
[:append]
)
IO.write("\r✅ Entrypoints Added\n")
opts
end
defp add_tag_to_index_template(opts) do
IO.write("Adding first tag to templates/pages/index.html.eex...")
Helpers.Phoenix.add_spa_tag_to_index_page_template!(opts)
IO.write("\r✅ Tag Added\n")
opts
end
defp run_last_check(opts) do
IO.write("Running last check...")
Helpers.Npm.run_npm_install!(opts)
IO.write("\r🚀 All set\n")
opts
end
end