How to compile nested templates of a single subdirectory

Hi.
I am working on a medium-sized Phoenix application. We have seen the compile times rise as we add more and more features to the application.

Some time ago I equipped myself with the improved mix xref that came with Elixir 1.6 and started to make sense of the module interdependencies that were causing our slow compile times.

Many of those have been already fixed, and compilation times have improved accordingly. I started working in the problems that slowed the compilation more, and there are still some other fixes to work on.

There is still a problem that I initially though was a low hanging fruit easy to fix, but it is giving me headaches.

In our web.ex file we have the following:

def view do
  quote do
    use Phoenix.View, root: "web/templates", pattern: "**/*"
  end
end

This pattern is telling Phoenix views to compile the templates in their corresponding directory AND all its subdirectories recursively. Due to the organization of our application in nested modules, there are many templates being compiled multiple times by views that never call them.

I am looking on optimising this. I know that by default phoenix uses the "*" pattern, which means that views only compile templates in the corresponding directory, but not in subdirectories.
Since we have many helper templates, I would prefer to have them stored in a component subdirectory (one for each view, that has the required helper templates). So I would have:

web/templates/my_view/component/table_header.html
web/templates/my_view/table.html

instead of

web/templates/my_view/_component_table_header.html
web/templates/my_view/table.html

The thing is, I don’t know how to make Phoenix views to compile templates in their corresponding directory AND in the component subdirectory.

I’ve tried multiple glob expressions. The last one being pattern: "{{.,component/**}/*" but I receive an (ErlangError) Erlang error: {:badpattern, :missing_delimiter} error.

Do you have any ideas?

2 Likes

For more information, in last instance this template finding is done by the Phoenix.Template.find_all/2 function[1], which calls Path.wildcard/1.

I am trying those same glob expressions with Path.wildcard/1 directly form the console, but I get the same error:

iex(1)> Path.wildcard("{.,component/**}*")
** (ErlangError) Erlang error: {:badpattern, :missing_delimiter}
    (stdlib) filelib.erl:62: :filelib.wildcard/2
    (elixir) lib/path.ex:642: Path.wildcard/2
  1. https://github.com/phoenixframework/phoenix/blob/5d83459c539f7f4172e9a76355e7a86b70bc537b/lib/phoenix/template.ex#L340-L347
1 Like

The glob in elixir does not support nesting like that, the item list can only contain specific items, not other globs. Why not just have two different subdirectories instead then use something like Path.wildcard("{global,components}/**") and have global store what is currently in . (no subdirectories unless you want?) and components store the tree of the rest?

Though I usually just make more *View modules instead. ^.^;

1 Like

Sometimes you can’t see the easiest and more evident approach until some one points you to it.

Making as many *View modules as needed is the best and simplest approach. This is the solution for my original question.
Thank you very much for your answer :bow:

2 Likes