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. Eject Fences are processed

mix-exs-dependency-removal

mix.exs Dependency Removal

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

blueprint-modifiers

Blueprint Modifiers

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 ->
  # `file` is a string containing the full file contents.
  # `app` is the `Uniform.App` struct. (The app being ejected.)
  # The string this function returns will be the ejected file contents.
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 my_ejectable_app:

  • 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

eject-fences

Eject Fences

In any .ex or .exs file, you can use "Eject 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

Eject Fence comments are always removed

Regardless of whether mix uniform.eject keeps or deletes the code in an eject fence, the eject 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.

eject-fences-for-other-languages

Eject Fences for other languages

Eject 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 Eject Fences for other languages or file types, you can do so using Uniform.Modifiers.eject_fences/3.

# eject fences for SQL files
modify ~r/\.sql$/, fn file, app ->
  eject_fences(file, app, "--")
end

# eject fences for Rust files
modify ~r/\.rs$/, &eject_fences(&1, &2, "//")

# eject fences for CSS files
modify ~r/\.css$/, &eject_fences(&1, &2, "/\\*", "\\*/")

disabling-code-transformations

Disabling Code Transformations

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.