The Plone Volto Monorepo
Not long ago, I’ve pushed for an essential change in the Volto repository. I want to explain why we did this and what benefits it brings.
I’ve started to write this post and since it was getting ridiculously long, I’ll decided to use a QA format intead. So here we go.
What?
We transformed the Volto repository in a full fledged JavaScript-based (and managed) monorepo.
This involved mainly two things: Instead of yarn, we use now pnpm as a package manager and the reorganization of the code in "workspaces".
The move to pnpm was in the bucket list since forever. The fact that it handles workspaces flawlessly and much better than yarn and npm helped to make the decission. It uses a non-flat node_modules
directory, and uses hard links to save disk space, and this approach makes much more sense to me than the npm and yarn approaches using a flat structure with hoisting if required.
Don't worry because pnpm command line is a superset of yarn's one and a few more useful ones. It's a breeze to make the switch. The only thing that you'll struggle is to type it quickly (hint: I created an alias).
I cetainly like one pnpm feature, which is to issue workspace commands with the --filter
command line argument.
The organization of the code changed also a bit, in order to fit the monorepo requirements. All packages now live under the packages
folder, including Volto itself. There is a new folder apps
that contains applications with example usage of Volto or it's satellite packages.
The rest is untouched, including the Makefile
convenience commands that proxy the needed commands to the right place.
Why?
Initially, Volto core was self-contained in the same package and namespace: @plone/volto
. Following the Plone core lesson learned, we avoided prematurely splitting the code, and preferred to have all the core under the same umbrella.
Then, the add-on story came and it opened the door to split things. Again, we decided willingly not to and maintain the core cohesive. There was simply no reason to do it.
At some point, it was necessary to have a shared i18n story extracted from core, so add-ons could use it in isolation from Volto. So @plone/scripts
was born. It is an independently released package used by Volto core and by Volto add-ons alike.
Eventually, Slate made into the core, technically was better to add it with the shape it had originally, so it was added as an special add-on, and the “core” add-ons concept was born. They were packages that were used by core, but never released. Webpack magic and the add-on registry hacks into it were in place to “fake” them, and the related package resolution.
We could have gone to the monorepo solution at that point, but it seemed overwhelmingly complex to make the move only because one core add-on. So we didn’t.
It was like this, until it stopped to make sense. Following the idea of using Volto technologies outside Volto, it made sense to start splitting features into their own packages. @plone/registry
along with other experimental packages like @plone/types
, @plone/components
and @plone/client
were born. Developing them on its own was a nightmare, so the idea of finally moving on and adopt a monorepo was born.
So, we removed the hacks, and moved using a monorepo setup so we could benefit of it and develop all these other packages at the same time as Volto.
How?
It's quite amazing how it all started to fall in the right place with a monorepo setup. You can declare dependencies between your monorepo packages in a way that you can specify a version or simply "take what's in there, right now" fashion. This is done by using:
So, the monorepo from that moment on, will take care of resolving your package using the reference to the current contents of the workspace that contains @plone/registry
. This is all pnpm
workspaces magic.
In fact, you can even drop a symlink to any add-on in there that you have in your machine, then add it as a dependency as shown above in Volto, and register it as an add-on, using the addons
key in package.json.
That easy.
Where?
The monorepo enabled also the addition of proof of concepts that demo the usage of Plone in other frameworks. You can find them in the apps
folder. These act also as monorepo workspaces and they use packages
from the monorepo.
Right now, PoCs of NextJS and Remix are in there, using both the experimental @plone/client
package. They also are playground of the upcoming @plone/components
and other experimental packages.