We’ll be making a Hanami 2.1.0.rc3 next month, and it will contain a new approach to asset compilation. I want to explain this here in detail so you can understand what’s coming, so you can know what to test with your apps, and to gather any feedback you may have!
Last year we released Hanami 2.1.0.rc1 and .rc2 and were preparing for the 2.1.0 final release when we encountered an issue with our assets handling: assets from different slices, but with the same base names would conflict and override each other in the final compiled
We explored a fix for this inside our hanami-assets JS package, and while we could have made that work, several downsides remained:
- We still required a duplicate (and incomplete) implementation of detecting Hanami slices
- We still required users to keep a redundant slice prefix in front of each slice’s assets (e.g.
- We still merged all slice assets into a single shared manifest, thereby breaking the isolation that slices otherwise provide
- It required us to keep and maintain a more complex esbuild wrapper, which would increase the chances of bugs or issues in the future.
- And because our usage of esbuild would have remained off its “golden path”, it increased our risk of hitting an evolutionary dead end for this overall approach.
Given all of this, we decided to step back and try a fresh approach to assets handling. I’ve now proven this approach will work, and I’d like to share it with you in advance of the upcoming rc3 release.
Previously, we invoked a single Node process that process that found all assets, compiled them with esbuild and then created a merged manifest. In the bug fix attempt I linked above, we looked at invoking an esbuild compilation per slice within the same single Node.js process (using async/await to run them in parallel).
With our new approach, our
hanami assets compile and
assets watch commands will invoke a single esbuild process per slice, and will pass the relevant slice paths as arguments to each esbuild process. We’ll handle this by forking to a Ruby process per slice.
In this approach, slices will be detected faithfully, since it’s our Ruby code that already knows about all registered slices and their locations.
esbuild compiles a single slice’s assets only, and outputs to a dedicated directory with a dedicated manifest
When our esbuild wrapper is invoked by the CLI, it will compile the assets within a single slice only, and output the compiled files to a dedicated directory for the slice:
- For the app, this is
- For each slice, this is
assets.json manifest will be written into each directory, and will contain entries for that slice’s assets only.
In this way, our usage of esbuild becomes much more typical: one input directory, one output directory. Nice and simple.
With every slice having its own assets directory and its own assets manifest, this also means that every slice will have its own
Hanami::Assets instance registered as a component with the
"assets" key, providing access to the assets in that slice’s manifest.
A slice can only access its own assets by default (with those assets no longer redundantly prefixed by the slice name)
Following from the above, this means a slice will have access only to its own assets by default.
This means e.g. that
Admin slice will reference the file at
Because each slice has its own assets component and its own underlying assets manifest, that means that you no longer need to provide the redundant slice name prefix that you had to do for the previous 2.1.0 release candidates. This is why you can write
Slices are meant to be serve as a means to isolate code, and the approach with assets will now take this same approach.
However, if you need to access assets from another slice, you can do so by:
- Importing the
"assets"component from the other slice
- Including the imported assets component as a dependency of your slice’s view context
- Referencing that assets object directly in your views/helpers:
This is not intended to be the mainstream approach, but the fact that it is possible should allow some flexibility where it is needed. See this PR for a clear demonstration.
Lastly, if you need to provide custom esbuild options for a particular slice’s asset compilation, you will now be able to do this via a custom
config/assets.js that will be picked up and used just for that slice. All you need to do is put the file inside the slice’s own
The app’s top-level
config/assets.js will be used for all slices that do not have their own custom config.
Check out the following work-in-progress PRs:
- Run multiple assets processes by timriley · Pull Request #131 · hanami/cli · GitHub
- Compile within single slice only by timriley · Pull Request #20 · hanami/assets-js · GitHub
- Require a root when initializing assets by timriley · Pull Request #136 · hanami/assets · GitHub
- Set up an assets provider per slice by timriley · Pull Request #1371 · hanami/hanami · GitHub
- Support cross slice assets by timriley · Pull Request #1372 · hanami/hanami · GitHub
I’m hoping for first half of February.
I’d love some solid testing for a week or two. If all goes well, 2.1.0 stable will come next.
Thanks for checking this out, and I hope this new approach can provide the most solid foundation for assets in Hanami 2. If you have any feedback, please share it here.