-
-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Igniter task to install Phoenix #140
base: main
Are you sure you want to change the base?
Changes from 3 commits
9a043b6
3ca5ef9
c430ecf
7ec291e
c1ab8da
831845e
7f2c38f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
defmodule Igniter.Phoenix.Generator do | ||
@moduledoc false | ||
# Wrap Phx.New.Generator | ||
# https://github.com/phoenixframework/phoenix/blob/7586cbee9e37afbe0b3cdbd560b9e6aa60d32bf6/installer/lib/phx_new/generator.ex#L69 | ||
|
||
def copy_from(igniter, project, mod, name) when is_atom(name) do | ||
mapping = mod.template_files(name) | ||
|
||
templates = | ||
for {format, _project_location, files} <- mapping, | ||
{source, target_path} <- files, | ||
source = to_string(source) do | ||
# target = Phx.New.Project.join_path(project, project_location, target_path) | ||
target = expand_path_with_bindings(target_path, project) | ||
{format, source, target} | ||
end | ||
|
||
Enum.reduce(templates, igniter, fn {format, source, target}, acc -> | ||
case format do | ||
:keep -> | ||
acc | ||
|
||
:text -> | ||
contents = mod.render(name, source, project.binding) | ||
Igniter.create_new_file(acc, target, contents, on_exists: :overwrite) | ||
leandrocp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
:config -> | ||
contents = mod.render(name, source, project.binding) | ||
config_inject(acc, target, contents) | ||
|
||
:prod_config -> | ||
contents = mod.render(name, source, project.binding) | ||
prod_only_config_inject(acc, target, contents) | ||
|
||
:eex -> | ||
contents = mod.render(name, source, project.binding) | ||
Igniter.create_new_file(acc, target, contents, on_exists: :overwrite) | ||
end | ||
end) | ||
end | ||
|
||
defp expand_path_with_bindings(path, project) do | ||
Regex.replace(Regex.recompile!(~r/:[a-zA-Z0-9_]+/), path, fn ":" <> key, _ -> | ||
project |> Map.fetch!(:"#{key}") |> to_string() | ||
end) | ||
end | ||
|
||
defp config_inject(igniter, file, to_inject) do | ||
patterns = [ | ||
""" | ||
import Config | ||
__cursor__() | ||
""" | ||
] | ||
|
||
Igniter.create_or_update_elixir_file(igniter, file, to_inject, fn zipper -> | ||
case Igniter.Code.Common.move_to_cursor_match_in_scope(zipper, patterns) do | ||
{:ok, zipper} -> | ||
{:ok, Igniter.Code.Common.add_code(zipper, to_inject)} | ||
|
||
_ -> | ||
{:warning, | ||
""" | ||
Could not automatically inject the following config into #{file} | ||
|
||
#{to_inject} | ||
"""} | ||
end | ||
end) | ||
end | ||
|
||
defp prod_only_config_inject(igniter, file, to_inject) do | ||
patterns = [ | ||
""" | ||
if config_env() == :prod do | ||
__cursor__() | ||
end | ||
""", | ||
""" | ||
if :prod == config_env() do | ||
__cursor__() | ||
end | ||
""" | ||
] | ||
|
||
Igniter.create_or_update_elixir_file(igniter, file, to_inject, fn zipper -> | ||
case Igniter.Code.Common.move_to_cursor_match_in_scope(zipper, patterns) do | ||
{:ok, zipper} -> | ||
{:ok, Igniter.Code.Common.add_code(zipper, to_inject)} | ||
|
||
_ -> | ||
{:warning, | ||
""" | ||
Could not automatically inject the following config into #{file} | ||
|
||
#{to_inject} | ||
"""} | ||
end | ||
end) | ||
end | ||
|
||
def gen_ecto_config(igniter, %{binding: binding}) do | ||
adapter_config = binding[:adapter_config] | ||
|
||
config_inject(igniter, "config/dev.exs", """ | ||
# Configure your database | ||
config :#{binding[:app_name]}, #{binding[:app_module]}.Repo#{kw_to_config(adapter_config[:dev])} | ||
""") | ||
end | ||
|
||
defp kw_to_config(kw) do | ||
Enum.map(kw, fn | ||
{k, {:literal, v}} -> ",\n #{k}: #{v}" | ||
{k, v} -> ",\n #{k}: #{inspect(v)}" | ||
end) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
defmodule Igniter.Phoenix.Single do | ||
@moduledoc false | ||
# Wrap Phx.New.Single | ||
# https://github.com/phoenixframework/phoenix/blob/7586cbee9e37afbe0b3cdbd560b9e6aa60d32bf6/installer/lib/phx_new/single.ex | ||
|
||
alias Phx.New.Project | ||
alias Igniter.Phoenix.Generator | ||
|
||
@mod Phx.New.Single | ||
|
||
def generate(igniter, project) do | ||
generators = [ | ||
{true, &gen_new/2}, | ||
{Project.ecto?(project), &gen_ecto/2}, | ||
{Project.html?(project), &gen_html/2}, | ||
{Project.mailer?(project), &gen_mailer/2}, | ||
{Project.gettext?(project), &gen_gettext/2}, | ||
{true, &gen_assets/2} | ||
] | ||
|
||
Enum.reduce(generators, igniter, fn | ||
{true, gen_fun}, acc -> gen_fun.(acc, project) | ||
_, acc -> acc | ||
end) | ||
end | ||
|
||
def gen_new(igniter, project) do | ||
Generator.copy_from(igniter, project, @mod, :new) | ||
end | ||
|
||
def gen_ecto(igniter, project) do | ||
igniter | ||
|> Generator.copy_from(project, @mod, :ecto) | ||
|> Generator.gen_ecto_config(project) | ||
end | ||
|
||
def gen_html(igniter, project) do | ||
Generator.copy_from(igniter, project, @mod, :html) | ||
end | ||
|
||
def gen_mailer(igniter, project) do | ||
Generator.copy_from(igniter, project, @mod, :mailer) | ||
end | ||
|
||
def gen_gettext(igniter, project) do | ||
Generator.copy_from(igniter, project, @mod, :gettext) | ||
end | ||
|
||
def gen_assets(igniter, project) do | ||
javascript? = Project.javascript?(project) | ||
css? = Project.css?(project) | ||
html? = Project.html?(project) | ||
|
||
igniter = Generator.copy_from(igniter, project, @mod, :static) | ||
|
||
igniter = | ||
if html? or javascript? do | ||
command = if javascript?, do: :js, else: :no_js | ||
Generator.copy_from(igniter, project, @mod, command) | ||
else | ||
igniter | ||
end | ||
|
||
if html? or css? do | ||
command = if css?, do: :css, else: :no_css | ||
Generator.copy_from(igniter, project, @mod, command) | ||
else | ||
igniter | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
defmodule Mix.Tasks.Igniter.InstallPhoenix do | ||
use Igniter.Mix.Task | ||
|
||
@example "mix igniter.install_phoenix" | ||
@shortdoc "Install Phoenix project files" | ||
|
||
@moduledoc """ | ||
#{@shortdoc} | ||
|
||
## Example | ||
|
||
```bash | ||
#{@example} | ||
``` | ||
|
||
## Options | ||
|
||
# TODO: phx.new options (--umbrella, --no-ecto, etc) | ||
# https://github.com/phoenixframework/phoenix/blob/7586cbee9e37afbe0b3cdbd560b9e6aa60d32bf6/installer/lib/mix/tasks/phx.new.ex#L13 | ||
""" | ||
|
||
def info(_argv, _source) do | ||
%Igniter.Mix.Task.Info{ | ||
group: :igniter, | ||
example: @example, | ||
positional: [:base_path] | ||
} | ||
end | ||
|
||
def igniter(igniter) do | ||
# TODO: check elixir version - https://github.com/phoenixframework/phoenix/blob/7586cbee9e37afbe0b3cdbd560b9e6aa60d32bf6/installer/lib/mix/tasks/phx.new.ex#L380 | ||
leandrocp marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
%{base_path: base_path} = igniter.args.positional | ||
|
||
# TODO: umbrella | ||
generate(igniter, base_path, {Phx.New.Single, Igniter.Phoenix.Single}, :base_path) | ||
end | ||
|
||
# TODO: opts | ||
# TODO: call validate_project(path) | ||
# TODO: perform some of the validations - https://github.com/phoenixframework/phoenix/blob/7586cbee9e37afbe0b3cdbd560b9e6aa60d32bf6/installer/lib/mix/tasks/phx.new.ex#L187 | ||
defp generate(igniter, base_path, {phx_generator, igniter_generator}, _path, opts \\ []) do | ||
project = | ||
base_path | ||
|> Phx.New.Project.new(opts) | ||
|> phx_generator.prepare_project() | ||
|> Phx.New.Generator.put_binding() | ||
|
||
igniter_generator.generate(igniter, project) | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -100,6 +100,7 @@ defmodule Igniter.MixProject do | |
{:spitfire, "~> 0.1 and >= 0.1.3"}, | ||
{:sourceror, "~> 1.4"}, | ||
{:jason, "~> 1.4"}, | ||
{:phx_new, "~> 1.7", runtime: false}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this okay? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 This one is tough. I don't actually think we need this. IIRC There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So we could have it as a test only dependency in that case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Should we have it as an optional dependency, in that case? That seems like the best of both worlds: It doesn't become a transient dependency that all apps using Igniter are required to fetch, but it still allows Igniter to test against it and specify a version spec that Igniter's compatible with. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🤔 I think that would make sense, yes. Since we'd be switching in our code on the module being compiled, it wouldn't matter if it's from a dep or the archive. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changed to |
||
# Dev/Test dependencies | ||
{:eflame, "~> 1.0", only: [:dev, :test]}, | ||
{:ex_doc, "~> 0.32", only: [:dev, :test], runtime: false}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
defmodule Mix.Tasks.Igniter.InstallPhoenixTest do | ||
use ExUnit.Case | ||
import Igniter.Test | ||
|
||
test "config_inject" do | ||
test_project() | ||
|> Igniter.compose_task("igniter.install_phoenix", ["my_app"]) | ||
|> assert_creates("lib/my_app_web/components/core_components.ex") | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some files do not have a valid string content, for eg binary files like
favicon.ico
but I think we still need to display that such file will be created. Might need to improve this code to detect updates as well.