Skip to content

ColocatedAssets (+ ColocatedCSS)#4138

Draft
SteffenDE wants to merge 5 commits intomainfrom
sd-colocated
Draft

ColocatedAssets (+ ColocatedCSS)#4138
SteffenDE wants to merge 5 commits intomainfrom
sd-colocated

Conversation

@SteffenDE
Copy link
Collaborator

Supersedes #4133.

So the idea here is to have a generic way to colocate "assets" (currently JS + CSS) where template parts are extracted to a phoenix-colocated folder with a specific structure:

_build/$MIX_ENV/phoenix-colocated/
_build/$MIX_ENV/phoenix-colocated/my_app/
_build/$MIX_ENV/phoenix-colocated/my_app/manifest
_build/$MIX_ENV/phoenix-colocated/my_app/MyAppWeb.DemoLive/line_HASH
_build/$MIX_ENV/phoenix-colocated/my_dependency/MyDependency.Module/line_HASH

This fits both colocated JS (hooks) and CSS, which can share the same folder layout using different manifest files. The question is if this is the right level of abstraction. It allows us to extract all of the common code into ColocatedAssets, but it assumes that all types of colocated assets can work with this layout.

I'm also not quite happy that the configured_callbacks in ColocatedAssets still need to hardcode the list of supported modules. It would be great if dependencies could (theoretically, the API is private for now) also implement colocated assets without us needing to change LiveView code. For example, I could imagine colocated CSS being implemented by annotating all tags with an attribute and then using a postcss plugin that rewrites the CSS rules (similar to https://github.com/SteffenDE/liveview_colocated_demo/blob/main/lib/colocated_demo_web/colocated_css.ex).

|> List.flatten()
end

defp maybe_link_node_modules! do
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Now one could argue that this is JavaScript specific, but you can also use @import to import from node_modules in CSS if using esbuild, so I guess it's fine?

Comment on lines +189 to +191
# Hardcoded for now
Phoenix.LiveView.ColocatedJS,
Phoenix.LiveView.ColocatedCSS
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm not too happy about this, but not sure what a better way would be.

Path.wildcard(Path.join(path, "*")) |> Enum.filter(&File.dir?(&1))
end

defp warn_for_outdated_config! do
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I decided to unify the configuration and deprecate the old key.


Colocated CSS uses the same folder structures as Colocated JS. See `Phoenix.LiveView.ColocatedJS` for more information.

To bundle and use colocated CSS with esbuild, you can import it like this in your `app.js` file:
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I created a PR for TailwindCSS to make this work with Tailwind out of the box (at least in newer versions as soon as it is released): tailwindlabs/tailwindcss#19617

@green-david
Copy link
Contributor

I'm also not quite happy that the configured_callbacks in ColocatedAssets still need to hardcode the list of supported modules. It would be great if dependencies could (theoretically, the API is private for now) also implement colocated assets without us needing to change LiveView code. For example, I could imagine colocated CSS being implemented by annotating all tags with an attribute and then using a postcss plugin that rewrites the CSS rules (similar to https://github.com/SteffenDE/liveview_colocated_demo/blob/main/lib/colocated_demo_web/colocated_css.ex).

@SteffenDE your other PR #4140 and this comment got me thinking... perhaps we could take a similar approach here as you are in #4140, but for ColocatedCSS, by providing a behaviour that people can implement to "transform" the CSS at compile-time and actually perform the scoping?

I can see people wanting to scope their CSS via other methodologies than using @scope - such as via postcss or some other strategy. The base ColocatedCSS module could be method-agnostic, and just worry about extracting the CSS content from the MacroComponent tag, feeding it to a module that implements the behaviour to scope/transform it, and then extracting the contents and manifest via ColocatedAssets.

I think this could also potentially give a way forward for ColocatedCSS that plays nicely with your idea of a separate experimental library for now - we could potentially merge the stripped-down version of ColocatedCSS itself without having to worry about the "gotchas" with the current @scope approach, and play around with different actual scoping strategies without having to modify LiveView itself or worry about maintaining an iffy scoping strategy. If we get one or more strategies in a really good spot that you feel confident about maintaining into the future, we could always upstream them into LiveView itself and provide those strategies officially out-of-the-box.

I am thinking perhaps the callback module could return a set of directives for ColocatedCSS to apply as well as the transformed CSS string, or something along those lines. We may also need to add an additional tag_attribute directive similar to root_tag_attribute that instead applies an attribute to all tags in the given template, as I think strategies like PostCSS would need that to work.

It might let us also kick the can down the road on your concerns about ColocatedJS and ColocatedCSS being hardcoded in the ColocatedAssets implementation, at least a bit.

Let me know what you think 🙂

@SteffenDE
Copy link
Collaborator Author

@green-david that sounds good to me! I'll adjust this PR (or send another one) some time next week :)

@green-david
Copy link
Contributor

@green-david that sounds good to me! I'll adjust this PR (or send another one) some time next week :)

@SteffenDE If you would like me to make a PR adding the tag_attribute directive, and/or put up a PR off this branch adding the behaviour and ripping out the @scope specific functionality, let me know! It may be easier for you to just do it (which I totally understand), but I know you are busy and figured id at least offer to take something off your plate :)

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.

2 participants