Current section

Files

Jump to
uniform lib mix tasks uniform.eject.ex
Raw

lib/mix/tasks/uniform.eject.ex

defmodule Mix.Tasks.Uniform.Eject do
@moduledoc """
Ejects an [Ejectable App](how-it-works.html#ejectable-apps) to a
standalone code repository.
## Examples
```bash
$ mix uniform.eject trillo
$ mix uniform.eject tweeter --confirm
$ mix uniform.eject hatmail --confirm --destination ../../new/dir
```
## Command line options
* `--destination` – output directory for the ejected code. Read the
[Configuration section of the Getting Started
guide](getting-started.html#configuration) to understand how the
destination is chosen if this option is omitted.
* `--confirm` – affirm "yes" to the prompt asking you whether you want to eject.
## Ejection Step By Step
When you eject an app by running `mix uniform.eject my_app`, the following happens:
1. The destination directory is created if it doesn't exist.
2. All files and directories in the destination are deleted, except for `.git`,
`_build`, and `deps`.
3. All files in `lib/my_app` and `test/my_app` are copied to the destination.
4. All files specified in the [base_files](Uniform.Blueprint.html#base_files/1) section of the
[Blueprint](`Uniform.Blueprint`) are copied to the destination.
5. All [Lib Dependencies](dependencies.html#lib-dependencies) of the app are
copied to the destination. This includes all of `lib/dep_name` and
`test/dep_name` automatically.
6. For each file copied, [a set of
transformations](./code-transformations.html) are applied to the file
contents – except for files specified with `cp` and `cp_r`.
7. `mix format` is ran on the ejected codebase.
In step 2, `.git` is kept to preserve the Git repository and history. `deps`
is kept to avoid having to download all dependencies after ejection. `_build`
is kept to avoid having to recompile the entire project after ejection.
In step 7, running `mix format` tidies up things like chains of newlines that
may appear from applying [Code Fences](code-transformations.html#code-fences).
It also prevents you from having to think about code formatting in
[modify](Uniform.Blueprint.html#modify/2).
"""
use Mix.Task
@doc false
def run(args) do
sample_syntax = " Syntax is: mix uniform.eject app_name [--destination path] [--confirm]"
args
|> OptionParser.parse!(strict: [destination: :string, confirm: :boolean])
|> case do
{opts, [app_name]} ->
eject_app(app_name, opts)
{_opts, []} ->
IO.puts("")
IO.puts(IO.ANSI.red() <> " No app name provided." <> sample_syntax)
IO.puts(IO.ANSI.yellow())
IO.puts(" Available apps:")
Uniform.ejectable_app_names() |> Enum.each(&IO.puts(" #{&1}"))
_unknown_options ->
IO.puts("")
IO.puts(IO.ANSI.red() <> " Too many options provided." <> sample_syntax)
end
end
defp eject_app(app_name, opts) do
app = Uniform.prepare(%{name: app_name, opts: opts})
IO.puts("")
IO.puts("πŸ—Ί Ejecting [#{app.name.camel}] to [#{app.destination}]")
IO.puts("")
IO.puts("πŸ€– Mix Dependencies")
app.internal.deps.included.mix
|> Enum.chunk_every(6)
|> Enum.each(fn mix_deps ->
IO.puts(" " <> Enum.join(mix_deps, " "))
end)
if Enum.empty?(app.internal.deps.included.mix) do
IO.puts(" " <> "[NONE]")
end
IO.puts("")
IO.puts("πŸ€“ Lib Dependencies")
app.internal.deps.included.lib
|> Enum.chunk_every(6)
|> Enum.each(fn lib_deps ->
IO.puts(" " <> Enum.join(lib_deps, " "))
end)
if Enum.empty?(app.internal.deps.included.lib) do
IO.puts(" " <> "[NONE]")
end
IO.puts("")
if Enum.any?(app.extra) do
IO.puts("πŸ“° Extra:")
app.extra
|> inspect()
|> Code.format_string!()
|> to_string()
|> String.replace(~r/^/m, " ")
|> IO.puts()
end
unless Keyword.get(opts, :confirm) == true do
IO.puts("")
IO.puts(
IO.ANSI.yellow() <>
" ⚠️ Warning: contents of the destination directory will be deleted" <>
IO.ANSI.reset()
)
end
eject =
if Keyword.get(opts, :confirm) == true do
true
else
Mix.shell().yes?("\n\nClear destination directory and eject?")
end
if eject do
IO.puts("")
Uniform.eject(app)
IO.puts("βœ… #{app.name.camel} ejected to #{app.destination}")
end
rescue
e in Uniform.NotEjectableError ->
message = Uniform.NotEjectableError.message(e)
IO.puts(IO.ANSI.yellow() <> message <> IO.ANSI.reset())
end
end