Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Project Templates #5505

Closed

Conversation

type1fool
Copy link
Contributor

@type1fool type1fool commented Jul 4, 2023

Goal

The goal of this PR is to provide better support for project customization for developers who frequently create new Phoenix projects.

Overview

Recently, I was reminded that we can customize the code produced by many of the Phoenix generators after revisiting @sasa1977's Medium blog series. While it has been possible to modify templates for most of Phoenix's Mix tasks, customizing the project file templates has proved to be more difficult.

Benefits

By allowing dev teams to customize the new project generator, significant time can be saved from adding common deps, config, CI yml, etc.

Limitations

While this PR allows non-standard files to be added to a template directory, these files are copied as-is without any EEx interpolation.

Example Usage

mix phx.new ~/projects/my_app --template-path ~/projects/my_phx_templates

~/my_phx_templates

image

~/projects/my_app

image

@chrismccord
Copy link
Member

Sorry I am not following how this is any different than the current behavior? We first look at the current app's priv dir for templates, and fallback to the phoenix ones. If this is about phx.new files, then having to clone and customize the templates isn't any different than customizing the templates in installer/templates. What am I missing? Thanks!

@type1fool
Copy link
Contributor Author

This PR only consolidates the templates into one directory, so project templates would live alongside other generator templates. I'm thinking through the changes required to support something like mix phx.new --template-dir ~/my_phx_new_templates.

@type1fool
Copy link
Contributor Author

Closing this for PR now. I want to experiment more with Mix.Generator and see if I can accomplish more of my goals at once.

@type1fool type1fool closed this Jul 4, 2023
@type1fool type1fool reopened this Jul 7, 2023
@type1fool
Copy link
Contributor Author

@chrismccord I reverted the commits that moved the project templates, and added commits to accept a new --template-path option in phx.new. This is much more powerful. Now, we can modify only the templates that need it and add extra files which will be copied into new projects.

Comment on lines +26 to +30
for {name, mappings} <- Module.get_attribute(env.module, :templates),
{format, _proj_location, files} <- mappings,
format != :keep,
{source, _target} <- files,
source = to_string(source) do
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's hard to see from the diff, but there was a nested for comprehension here. I merged them into one.

@type1fool type1fool changed the title Move Project Templates Custom Project Templates Jul 8, 2023
@josevalim
Copy link
Member

Thanks @type1fool! My concern is that this couples all of the generator internals and variables with user code. And any refactoring we do may break your generators. In other words, it makes all of the generator internals public.

I think we should instead promote init generator that you run afterwards. For example:

mix phx.new foo
cd foo
mix my_lib.init

@type1fool
Copy link
Contributor Author

Thank you for reviewing, @josevalim. I'll look for examples of packages using an init task. My first thought is that inserting code lines into the generated project files sounds a bit more complex. Would an init task be less susceptible to changes in the generated code?

@josevalim
Copy link
Member

The contact surface would be smaller. An init task has to interact with the generated files. Custom templates would interact with all generated files and all internal assigns used by phx.new.

@type1fool
Copy link
Contributor Author

I'm planning on revisiting this once I get some bandwidth to experiment more with modifying existing files with Mix tasks. @josevalim do you have an example of a package that does this?

While I'm here, the Thinking Elixir guys discussed this topic in the latest episode:

https://podcast.thinkingelixir.com/167

It seems there's an appetite to have better tools for customizing the code generated by phx_new. That could mean more documentation about using tasks or maybe the custom template path flag I'm proposing.

@type1fool
Copy link
Contributor Author

type1fool commented Sep 5, 2023

+ @victorbjorklund @dbernheisel @brainlid

@type1fool
Copy link
Contributor Author

I haven't forgotten about this PR. In my webauthn_components project, I've been building generators. Generating new files is quite easy since the templates use EEx. Injecting code into existing Elixir files was a bit more complicated, but I was able to get a nice solution using Sourceror. I even resorted to Regex to inject a few JS hooks into app.js.

These tools may be sufficient to do what I want at a larger scale for client projects now that I'm more comfortable with Sourceror and Elixir's ASTs. I do think there is still an appetite in the community for customizing the project generators. All of the approaches seem to require some level of maintenance over time.

@frankdugan3
Copy link
Contributor

I'm planning on revisiting this once I get some bandwidth to experiment more with modifying existing files with Mix tasks. @josevalim do you have an example of a package that does this?

Surface UI has an installer that mods existing files (using Sourceror) from a default phx.new project.

@josevalim
Copy link
Member

Closing this, as the Surface approach is most likely the best way to forward. :)

@josevalim josevalim closed this Dec 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants