View Source Code Transformations

During mix uniform.eject, there are 4 code transformations applied to file contents. These transformations happen to every file, except those ejected with cp and cp_r.

They occur in this order.

  1. Unused mix.exs Dependencies are Removed
  2. Blueprint Modifiers are ran
  3. The Base Project Name is replaced with the ejected app's name
  4. Code Fences are processed

Disabling Code Transformations for a file

If you have a file that should not have Code Transformations applied upon ejection, use cp instead of file.

If there is an entire directory of contents that should not be modified, use cp_r, which will be much faster.

mix-exs-dependency-removal

mix.exs Dependency Removal

Any Mix Dependency that is not directly or indirectly required by the app via mix.exs or the Blueprint module is removed from the ejected mix.exs.

modifiers-from-the-ejection-blueprint

Modifiers from the Ejection Blueprint

Users can specify arbitrary modifications that should be applied to various files using the modify macro in the Blueprint module:

modify ~r/.+_worker.ex/, fn file, app ->
  # This code will be ran for every file whose relative path in the base
  # project matches the regex.
  #
  # `file` is a string containing the full file contents.
  #
  # `app` is the `Uniform.App` struct of the given app being ejected.
  #
  # This is essentially a function body, must return a string with
  # the modified file contents to eject.
end

modify "lib/my_app_web/router.ex", fn file ->
  # This modifier is like the one above, but the transformation will only
  # be ran for `lib/my_app_web/router.ex`.
end

replacing-the-base-project-name

Replacing the Base Project Name

The base project name, appearing anywhere in a file, is replaced by the ejected app name. This applies to the following formats: base_app, base-app, and BaseApp.

The base project name is the :app key returned by project in the mix.exs file of the Base Project. (For example, :my_base_app below.)

# mix.exs
def project do
  [
    app: :my_base_app, # <- base project name
    ...
  ]
end

Given the above mix.exs, if you were to run mix uniform.eject MyEjectableApp:

  • my_base_app would be replaced with my_ejectable_app
  • my-base-app would be replaced with my-ejectable-app
  • MyBaseApp would be replaced with MyEjectableApp

Replacement in file paths

This same replacement of base_project_name to ejected_app_name also occurs in file paths, but only with this_format. (Not this-format or ThisFormat.)

This means a file at lib/base_project_name/foo.ex would be ejected to lib/ejected_app_name/foo.ex.

This means that a file like this

defmodule MyBaseAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_base_app

  socket "/socket", MyBaseAppWeb.UserSocket,
    websocket: true,
    longpoll: false

  plug MyBaseAppWeb.Router
end

Would be transformed to this

defmodule MyEjectableAppWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :my_ejectable_app

  socket "/socket", MyEjectableAppWeb.UserSocket,
    websocket: true,
    longpoll: false

  plug MyEjectableAppWeb.Router
end

code-fences

Code Fences

In any .ex or .exs file, you can use "code fence" comments to remove code unless certain criteria are met.

To remove code unless the ejected app depends on a Lib Dependency called my_lib, wrap it in these comments:

# uniform:lib:my_lib
# ... code
# /uniform:lib:my_lib

To remove code unless the ejected app depends on a Mix Dependency called absinthe, wrap it in these comments:

# uniform:mix:absinthe
# ... code
# /uniform:mix:absinthe

To remove code unless the ejected app is called MyApp, wrap it in these comments:

# uniform:app:my_app
# ... code
# /uniform:app:my_app

Finally, to always remove a chunk of code whenever ejection happens, wrap it in these comments:

# uniform:remove
# ... code
# /uniform:remove

Code Fence comments are removed on ejection

Note that regardless of whether mix uniform.eject keeps or deletes the code in a code fence, the code fence comments themselves (like # uniform:app:my_app) are always removed.

Furthermore, mix uniform.eject runs mix format on the ejected codebase at the end. So you always end up with "clean" looking code.

code-fences-for-other-languages

Code Fences for other languages

Code Fences are also processed for .js/.ts/.jsx/.tsx files using JS single-line comments.

// uniform:lib:my_lib
// ...
// /uniform:lib:my_lib

If you would like to support Code Fences for other languages or file types, you can do so using Uniform.Blueprint.modify/2 and Uniform.Modifiers.code_fences/3.

# code fences for SQL files
modify ~r/\.sql$/, fn file, app ->
  Uniform.Modifiers.code_fences(file, app, "--")
end

# code fences for Rust files
modify ~r/\.rs$/, fn file, app ->
  Uniform.Modifiers.code_fences(file, app, "//")
end