Packages
phx_new
1.4.2
1.8.9
1.8.8
1.8.7
1.8.6
1.8.5
1.8.4
1.8.3
1.8.2
1.8.1
1.8.0
1.8.0-rc.4
1.8.0-rc.3
1.8.0-rc.2
1.8.0-rc.1
1.8.0-rc.0
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.7.0-rc.3
1.7.0-rc.2
1.7.0-rc.1
1.7.0-rc.0
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-rc.1
1.6.0-rc.0
1.5.15
1.5.14
1.5.13
1.5.12
1.5.11
1.5.10
1.5.9
1.5.8
1.5.7
1.5.6
1.5.5
1.5.4
1.5.3
1.5.2
1.5.1
1.5.0
1.5.0-rc.0
1.4.18
1.4.17
1.4.16
1.4.15
1.4.14
1.4.13
1.4.12
1.4.11
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-rc.3
1.4.0-rc.2
1.4.0-rc.1
1.4.0-rc.0
1.4.0-dev.0
1.3.5
Phoenix framework project generator. Provides a `mix phx.new` task to bootstrap a new Elixir application with Phoenix dependencies.
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/phx.new.ex
defmodule Mix.Tasks.Phx.New do
@moduledoc """
Creates a new Phoenix project.
It expects the path of the project as an argument.
mix phx.new PATH [--module MODULE] [--app APP]
A project at the given PATH will be created. The
application name and module name will be retrieved
from the path, unless `--module` or `--app` is given.
## Options
* `--umbrella` - generate an umbrella project,
with one application for your domain, and
a second application for the web interface.
* `--app` - the name of the OTP application
* `--module` - the name of the base module in
the generated skeleton
* `--database` - specify the database adapter for Ecto. One of:
* `postgres` (https://github.com/elixir-ecto/postgrex)
* `mysql` (https://github.com/xerions/mariaex)
* `mssql` (https://github.com/findmypast-oss/mssqlex)
Please check the driver docs, between parentheses, for more information
and requirements. Defaults to "postgres".
* `--no-webpack` - do not generate webpack files
for static asset building. When choosing this
option, you will need to manually handle
JavaScript dependencies if building HTML apps
* `--no-ecto` - do not generate Ecto files.
* `--no-html` - do not generate HTML views.
* `--binary-id` - use `binary_id` as primary key type
in Ecto schemas
* `--verbose` - use verbose output
When passing the `--no-ecto` flag, Phoenix generators such as
`phx.gen.html`, `phx.gen.json` and `phx.gen.context` may no
longer work as expected as they generate context files that rely
on Ecto for the database access. In those cases, you can pass the
`--no-context` flag to generate most of the HTML and JSON files
but skip the context, allowing you to fill in the blanks as desired.
Similarly, if `--no-html` is given, the files generated by
`phx.gen.html` will no longer work, as important HTML components
will be missing.
## Examples
mix phx.new hello_world
Is equivalent to:
mix phx.new hello_world --module HelloWorld
Or without the HTML and JS bits (useful for APIs):
mix phx.new ~/Workspace/hello_world --no-html --no-webpack
As an umbrella:
mix phx.new hello --umbrella
Would generate the following directory structure and modules:
hello_umbrella/ Hello.Umbrella
apps/
hello/ Hello
hello_web/ HelloWeb
You can read more about umbrella projects using the
official [Elixir guide](http://elixir-lang.org/getting-started/mix-otp/dependencies-and-umbrella-apps.html#umbrella-projects)
To print the Phoenix installer version, pass `-v` or `--version`, for example:
mix phx.new -v
"""
use Mix.Task
alias Phx.New.{Generator, Project, Single, Umbrella, Web, Ecto}
@version Mix.Project.config[:version]
@shortdoc "Creates a new Phoenix v#{@version} application"
@switches [dev: :boolean, webpack: :boolean, ecto: :boolean,
app: :string, module: :string, web_module: :string,
database: :string, binary_id: :boolean, html: :boolean,
umbrella: :boolean, verbose: :boolean]
def run([version]) when version in ~w(-v --version) do
Mix.shell.info("Phoenix v#{@version}")
end
def run(argv) do
elixir_version_check!()
case parse_opts(argv) do
{_opts, []} -> Mix.Tasks.Help.run(["phx.new"])
{opts, [base_path | _]} ->
generator = if opts[:umbrella], do: Umbrella, else: Single
generate(base_path, generator, opts)
end
end
def run(argv, generator) do
elixir_version_check!()
case parse_opts(argv) do
{_opts, []} -> Mix.Tasks.Help.run(["phx.new"])
{opts, [base_path | _]} -> generate(base_path, generator, opts)
end
end
def generate(base_path, generator, opts) do
base_path
|> Project.new(opts)
|> generator.prepare_project()
|> Generator.put_binding()
|> validate_project()
|> generator.generate()
|> prompt_to_install_deps(generator)
end
defp validate_project(%Project{opts: opts} = project) do
check_app_name!(project.app, !!opts[:app])
check_directory_existence!(project.project_path)
check_module_name_validity!(project.root_mod)
check_module_name_availability!(project.root_mod)
project
end
defp prompt_to_install_deps(%Project{} = project, generator) do
install? = Mix.shell.yes?("\nFetch and install dependencies?")
cd_step = ["$ cd #{relative_app_path(project.project_path)}"]
maybe_cd(project.project_path, fn ->
mix_step = install_mix(project, install?)
compile =
case mix_step do
[] -> Task.async(fn -> rebar_available?() && cmd(project, "mix deps.compile") end)
_ -> Task.async(fn -> :ok end)
end
webpack_step = install_webpack(install?, project)
Task.await(compile, :infinity)
if Project.webpack?(project) and !System.find_executable("npm") do
print_webpack_info(project, generator)
end
print_missing_steps(cd_step ++ mix_step ++ webpack_step)
if Project.ecto?(project) do
print_ecto_info(project, generator)
end
print_mix_info(generator)
end)
end
defp maybe_cd(path, func), do: path && File.cd!(path, func)
defp parse_opts(argv) do
case OptionParser.parse(argv, strict: @switches) do
{opts, argv, []} ->
{opts, argv}
{_opts, _argv, [switch | _]} ->
Mix.raise "Invalid option: " <> switch_to_string(switch)
end
end
defp switch_to_string({name, nil}), do: name
defp switch_to_string({name, val}), do: name <> "=" <> val
defp install_webpack(install?, project) do
assets_path = Path.join(project.web_path || project.project_path, "assets")
webpack_config = Path.join(assets_path, "webpack.config.js")
maybe_cmd(project, "cd #{relative_app_path(assets_path)} && npm install && node node_modules/webpack/bin/webpack.js --mode development",
File.exists?(webpack_config), install? && System.find_executable("npm"))
end
defp install_mix(project, install?) do
maybe_cmd(project, "mix deps.get", true, install? && hex_available?())
end
defp hex_available? do
Code.ensure_loaded?(Hex)
end
defp rebar_available? do
Mix.Rebar.rebar_cmd(:rebar) && Mix.Rebar.rebar_cmd(:rebar3)
end
defp print_webpack_info(_project, _gen) do
Mix.shell.info """
Phoenix uses an optional assets build tool called webpack
that requires node.js and npm. Installation instructions for
node.js, which includes npm, can be found at http://nodejs.org.
The command listed next expect that you have npm available.
If you don't want webpack, you can re-run this generator
with the --no-webpack option.
"""
end
defp print_missing_steps(steps) do
Mix.shell.info """
We are almost there! The following steps are missing:
#{Enum.join(steps, "\n ")}
"""
end
defp print_ecto_info(%Project{}, Web), do: :ok
defp print_ecto_info(%Project{app_path: nil}, _gen), do: :ok
defp print_ecto_info(%Project{app_path: app_path} = project, _gen) do
config_path =
app_path
|> Path.join("config/dev.exs")
|> Path.relative_to(project.project_path)
Mix.shell.info """
Then configure your database in #{config_path} and run:
$ mix ecto.create
"""
end
defp print_mix_info(gen) when gen in [Ecto] do
Mix.shell.info """
You can run your app inside IEx (Interactive Elixir) as:
$ iex -S mix
"""
end
defp print_mix_info(_gen) do
Mix.shell.info """
Start your Phoenix app with:
$ mix phx.server
You can also run your app inside IEx (Interactive Elixir) as:
$ iex -S mix phx.server
"""
end
defp relative_app_path(path) do
case Path.relative_to_cwd(path) do
^path -> Path.basename(path)
rel -> rel
end
end
## Helpers
@doc false
def recompile(regex) do
if Code.ensure_loaded?(Regex) and function_exported?(Regex, :recompile!, 1) do
apply(Regex, :recompile!, [regex])
else
regex
end
end
defp maybe_cmd(project, cmd, should_run?, can_run?) do
cond do
should_run? && can_run? ->
cmd(project, cmd)
should_run? ->
["$ #{cmd}"]
true ->
[]
end
end
defp cmd(%Project{} = project, cmd) do
Mix.shell.info [:green, "* running ", :reset, cmd]
case Mix.shell.cmd(cmd, cmd_opts(project)) do
0 ->
[]
_ ->
["$ #{cmd}"]
end
end
defp cmd_opts(%Project{} = project) do
if Project.verbose?(project) do
[]
else
[quiet: true]
end
end
defp check_app_name!(name, from_app_flag) do
unless name =~ recompile(~r/^[a-z][\w_]*$/) do
extra =
if !from_app_flag do
". The application name is inferred from the path, if you'd like to " <>
"explicitly name the application then use the `--app APP` option."
else
""
end
Mix.raise "Application name must start with a letter and have only lowercase " <>
"letters, numbers and underscore, got: #{inspect name}" <> extra
end
end
defp check_module_name_validity!(name) do
unless inspect(name) =~ recompile(~r/^[A-Z]\w*(\.[A-Z]\w*)*$/) do
Mix.raise "Module name must be a valid Elixir alias (for example: Foo.Bar), got: #{inspect name}"
end
end
defp check_module_name_availability!(name) do
[name]
|> Module.concat()
|> Module.split()
|> Enum.reduce([], fn name, acc ->
mod = Module.concat([Elixir, name | acc])
if Code.ensure_loaded?(mod) do
Mix.raise "Module name #{inspect mod} is already taken, please choose another name"
else
[name | acc]
end
end)
end
defp check_directory_existence!(path) do
if File.dir?(path) and not Mix.shell.yes?("The directory #{path} already exists. Are you sure you want to continue?") do
Mix.raise "Please select another directory for installation."
end
end
defp elixir_version_check! do
unless Version.match?(System.version, "~> 1.5") do
Mix.raise "Phoenix v#{@version} requires at least Elixir v1.5.\n " <>
"You have #{System.version()}. Please update accordingly"
end
end
end