Phoenix Asset Management - Watchers

I am finding it a bit difficult to understand asset management in Phoenix - Asset Management — Phoenix v1.6.6 (hexdocs.pm) - I was going through the watchers part.

I am using vitejs to bundle assets. I have a vite watch task defined in package.json
So, in my config/dev.exs I have a watcher defined
npm: ["run", "watch", cd: Path.expand("../assets", __DIR__)]
I understood the above like, keep watching the assets directory - any changes there, run the vite watch again, so that assets get rebuilt. In fact, it is happening exactly like that.
Similarly, in the same config/dev.exs we have live_reload which watches the phoenix/elixir part of files and reloads the browser. So, any changes I make to the markup etc. this reload does take care.
Now, if we are using tailwindcss, a change in the heex files, should also trigger the vite watch task because, if we add any new styles in the templates, the new styles have to be added by the vite parser - mainly because of JIT nature of tailwindcss.
Now, my question is - how do we tell the watcher in the config/dev.exs that it has to rebuild or call the vite watch task even when we change the template files?
Presently, if I end the mix phx.server task and restart again, the styles are getting reflected. Otherwise, it is not working well.

2 Likes

I think that’s out-of-scope for the dev watchers config, per the docs

:watchers - a set of watchers to run alongside your server. It expects a list of tuples containing the executable and its arguments. Watchers are guaranteed to run in the application directory

Basically the :watchers option just says “hey also run these things”, but it doesn’t actually “watch” for changes and then run them.

So what you want to do is configure vite watch to also track *.heex file changes in lib/ (or the priv/static/assets/**.css or whatever is the actual change you want to get picked up).

I think the term might be a bit confusingly labelled but there probably also isn’t a better choice.

2 Likes

Could I explain my problem clearly?

My understanding is that @soup’s answer is correct. You have to ask Vite JS to watch the template files too.

1 Like

Sorry, if this is looking down right simple and silly.

  1. We did not add the template files to the watch task - when we were using webpack or tailwind-cli - still it was working. Any change between these two tools?
  2. I tried specifying the template files as a glob lib/**/*.*ex - and - the live reload is taking close to 10 seconds or more to reload the page - with unpredictable results. Sometimes, the new styles are getting picked up and sometimes, only cached styles are working.
  3. With tailwind standalone we were putting
    tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]} in the config/dev.exs - that ~w(--watch) watches which folders? I guess, I need to specify something similar.

The default webpack generator would have been pre-configured with the right paths probably.

  • I tried specifying the template files as a glob lib/**/*.*ex - and - the live reload is taking close to 10 seconds or more to reload the page - with unpredictable results. Sometimes, the new styles are getting picked up and sometimes, only cached styles are working.

Sometimes I see this with tailwind because the JIT used to write its results out in parts, you can configure the live reloader to wait an interval before it messages phoenix: GitHub - phoenixframework/phoenix_live_reload: Provides live-reload functionality for Phoenix

  • With tailwind standalone we were putting
    tailwind: {Tailwind, :install_and_run, [:default, ~w(--watch)]} in the config/dev.exs - that ~w(--watch) watches which folders? I guess, I need to specify something similar.

Tailwind will be using tailwind.conf.js’s content: key to know what to look for. I think it’s content, it used to be purge until 3.0. That file may not exist, tailwind might use a default one if it is missing.

Probably your best bet is to remove dev.ex and the live reloader from the equation first. Get your tailwind and vite builders and watchers working manually from the command line, in a way that doesn’t fight each other, then just copy the setup into dev.ex, and where ever you are also running them for prod builds.

1 Like

My advice may not solve your problem. I don’t use tailwind’s utility classes in my templates directly; I always wrap them in my custom css file into synmatic classes using @apply. This way you would have a cleaner template file, also the watcher problem just disappears

1 Like

Thanks for the detailed explanations @soup !
I think, I got a lot of new knowledge irrespective of what happens to my problem. :slight_smile:
I was actually trying parcel-bundler/parcel-css: A CSS parser, transformer, and minifier written in Rust. (github.com) to process the CSS part of my Phoenix application.
I was looking for such an alternative because - tailwind standalone does not honor the postcss.config.js - and - it looks like the standalone is not supporting nested css.
To cut long things short - I am running parcel watch as a npm task - and - added the npm watch as a watcher in config/dev.exs - This setup is working well and fast when we start the server - however, parcel is not recompiling when I change the template files, even though tailwind.config.js is present and contains the content clause.
Yeah - looks like too many tools clashing with each other. :frowning:
On a side note - probably, we need a nice CSS framework which does not depend on Javascript. Though tailwindcss is a CSS only framework without any JS - it relies so heavily on JS toolchain that, it is a big put-off.
May be there is an inherent dichotomy - Phoenix works great without node and the partnering tailwindcss is heavily reliant on JS toolchain.
Hope a better solution is around the horizon.

I fully agree. May be we do need a library with sane @apply statements - something similar to daisyUI — Tailwind CSS Components
But, the problem here is not the watcher on the assets folder. It is more on the Phoenix Lib folder.

If you have 100% synmatic classes in your template, you don’t need to watch the lib dir from the tailwind’s side. The lib and the cas would’ve been decoupled.

1 Like

Beautiful. Did not think that way. I will try that. Unfortunately Reusing Styles - Tailwind CSS Tailwind authors do not believe in it.
Looks like for Phoenix LiveView Bulma: Free, open source, and modern CSS framework based on Flexbox is the best suited CSS framework. No JS, minimal tooling - modern - can be processed with DartSass.

You don’t have to reuse anything. With tailwindcss/nesting you could replicate your html structure 100% in your css and use zero class name. More likely though, you can settle in between of reusing and non-reusing in a zone that you feel conformable. It is a legal and supported usage of tailwind. I use tailwind not because of I agree with the authors philosophically; it has good documentation and is generally not opinionated, that’s all.

Also, I like tailwind now; but I still like to reserve the right of ditching tailwind in the future. With the css and template fully decoupled I could do so with minimum effort.

1 Like

[quote=“derek-zhou, post:12, topic:45661, full:true”]
With tailwindcss/nesting you could replicate your html structure 100% in your css and use zero class name. [/quote]
Can you please expand a bit on this? May be some small example will help.

Agree with the rest of things.

This project is still using tailwind 2, but the idea is the same.

postcss can be nested in the same way of sass. So in the extreme case, you could match your css to your html and be very specific. In the above example, I was using a middle ground though.

1 Like

Looks like this problem is for real. Tailwind v3 PostCSS Plugin: --watch does not rebuild CSS on changes to content (HTML) files · Issue #7136 · tailwindlabs/tailwindcss (github.com)
@soup - I think you are right. vite/parcel/tailwind all of them watch for the content using tailwind.config.js only - and - no need to add the template files to the watch list.

I have created an issue here Tailwind integration into Phoenix Framework - watch not rebuilding changes to content · Issue #7634 · parcel-bundler/parcel (github.com)
Just recording it here - someone might find it useful.

I was surprised by this. I’m working on an Elm installer/compiler based on phoenixframework/esbuild and phoenixframework/tailwind but I’m having to build out the watching myself because the elm binary doesn’t have a --watch-like mode.

Maybe I’m missing something, but if Phoenix would also watch for changes then each delegated binary wouldn’t have to watch on its own.

I’d prefer not to wrap watchexec or entr and it seems overly complicated to start a process and use file_system.

Hmm, maybe I was looking in the wrong place and I should instead be using something more like phoenix_live_reload.

How does Phoenix live reload differ from updating an asset in assets dir and letting Phoenix react to that? … Actually, I don’t understand how Phoenix knows to reload a page when a js asset has updated. Wouldn’t Phoenix have to parse the html and determine that a script was included in a page?

After further thought, I believe Phoenix will reload server rendered templates, but it won’t reload assets like js files unless you use the phoenix live reload library and specify which assets to perform live reload.