Closed adamziel closed 1 year ago
Adding @azaozz @desrosj @hellofromtonya and @SergeyBiryukov to the CC list.
I'll ponder this a little more before committing any notes to the ticket.
Hi @adamziel Thanks for creating this issue and starting this discussion, it's always good to reassess past decision and see whether they're still good decision for what has to come.
Before doing so though, it's important that we decontextualize how the architecture of Gutenberg came to be, the history and the reasoning behind it as the issue right now seem to go directly into a "solution" by omitting a lot of context.
First, it's important to note that Gutenberg has been built from the start as packages, in fact, originally it was organized as a single monolith, using folders, very close to the suggestion here. You can check old tags like here.
Quickly though, some use-cases started to emerge:
We wanted to be able to share scripts with other Core features and use some core scripts (api-request which became api-fetch for instance, a11y script which became a11y package...) => this led the creation of the @wordpress/packages
repository, a repository of reusable packages that can be used both in Gutenberg and WordPress. Ultimately, the maintenance of two separate repositories both updating related scripts (Gutenberg and packages repository) become too difficult which led to the decision of merging all packages/scripts in a single repository (Gutenberg repository).
We wanted to be able to use components and UI from gutenberg in other third-party pages, settings pages... without necessarily having to load the whole editor scripts
We wanted to offer a way to build several kind of block editors: comments, post editor, third-party editors... (later, widgets editor, navigator editor, site editor).
This led us to the necessity of modularizing our code into semantically valid units which increased by an order of magnitude the separation of concerns in the project. You mention that it's not perfect, and I agree but I think it's decent and in fact the big issues we might have right now pre-dates the introduction of packages (block-library, editor package).
I've seen a number of times that the packages force people to think where to put components, hooks, where's the right place for things. At start, people get frustrated about not being able to just put the code wherever they want and ship but that's a good thing to force people to think about the modularization and separation of concerns. In fact, with the initial folder based architecture, we were supposed to have folders with dedicated roles but people fell too easily in the trap of just mixing stuff together. (For instance using post related data in block editor specific components...)
wp
globals, which was also used as a communication layer between core packages, similarly to how third-party developers use it.Next, came the integration with Core, and how to move the code into Core. Packages were not the only option explored, in fact, we also tried git submodules but npm packages ended up being the best option there.
Now, later third-party npm users benefited from all of these packages to build integrations in Tumblr, Drupal, Laravel and way more...
There's also the merge of the Gutenberg packages with the mobile packages which I know has helped the folks working on the mobile applications a lot. I'm not entirely familiar with this area, so we'd need their input here as well.
Each one of these steps were discussed in #core-js meetings between several folks, at the time these meetings were very lively and productive.
This is to say, that any change we make here, we should be mindful of all these requirements that accumulated over time:
1- Enforced awareness and listing rules to guarantee separation of concerns (and modularity) as much as we can. 2- Shared scripts between the plugin and WordPress Core. 3- The possibility to load a small chunk (for instance just the components, or just the a11y utils) in a particular wp-admin page, be it an editor page or not. 4- Keeping the existing wp globals largely untouched: Any major change would impact backward compatibility way too much. 5- Being able to use our modules using npm for third-party consumers (in fact whether it's one module or multiple here doesn't matter)
The proposed solution is basically to just merge all the packages inside one using folders, so:
@wordpress/components
becomes @wordpress/gutenberg/components
in fact, it can even be just wordpress/components
(components folder inside a wordpress package, there's no need for a "gutenberg package", since these are not editor specific and gutenberg is just a code name)@wordpress/x
becomes wordpress/x
If I'm understand properly, we just publish a single package to npm instead of publishing multiple ones.
So now, let's analyze the proposed solution and the promises above:
Separate packages enable surgically using specific parts of Gutenberg A single package would make it even easier. Figuring out which of the 40+ packages are needed is a hit-and-miss trial-and-error process.
There's an issue right now, which I'd call a "bootstrapping" issue, in the sense that if you want to build a block editor, you need a provider, to load blocks, to load formats, and have some providers... and these can come from several packages. Most of these are optional because they are exchangeable: you can decide to not load blocks and bring your own, bring your own formats, load shortcuts or not... You guessed it, it's not easy to bootstrap an editor. Moving to a single package though won't change anything to the fact that you still need to figure out what you need to load and what you don't need to load, the only thing that changes is that you'll do npm install
only once which will bring all the packages in one go (including the ones you don't need: think all the WP specific code, all the mobile code, all the dev tools you might not need...)
The bundler would take care of that automatically with tree shaking. The package consumer would import the bits they are interested in, and the bundler would remove all the irrelevant code. If two entrypoints require the same package, the bundler would move it to a separate chunk to avoid downloading the same code twice. A single package could still produce the same set of JavaScript files as the existing 40+ packages.
In the current approach, we do this by marking all @wordpress/*
packages as externals, so every time the bundler finds @wordpress/x
it's transformed automatically to wp.x
global variable and a dependency is added in php and each one of these entry points (packages now, but I guess top level folders in the proposed solution) creates a new wp.x
global (a webpack config).
So basically, with the new solution, we'd have to find a way to recreate this behavior using webpack or any other bundler. I think it's probably the exact same config we have now, expect the name of the externals to change (assuming folders work as externals too which I'm not sure about and in which case we'd need to build a custom bundler plugin)
Private experimental APIs would no longer be an issue – they would never get publicly exported in the first place
Unfortunately, I don't think this solves that at all. Here's an example. I want to introduce a new Hello
component inside the components folder (since it's a reusable UI component for SoC) and I want to use the Button
component but I want Hello
to be internal and only used inside block-editor
folder for now.
The block editor code would do something like import Hello from '/components/hello'
:
And we did see in the previous point that if we are to generate the same chunks (and globals) we have now, every time we find /components/hello
we'll turn it into wp.components.Hello
.
The bundler doesn't have any knowledge that allow him to differentiate "internal" components from "public" ones, both are imported in the same way.
So far, for me, the only advantage that is clear is the removal of complex lerna publishing and scripts... (assuming we keep a single package of course). I also feel this is largely underestimating the effort it takes to accomplish what it is proposed. At this point at least, I feel it's a giant project for a small outcome unfortunately.
The "bootstrapping" issue is a real pain point though and a less impactful and way more straightforward solution is the "proxy" package that is proposed in the alternatives above. In fact, it's an approach that has already been taken by some third-party developers including the Drupal folks that built their own "proxy" package here or the isolated block editor by a8c. An official "proxy/bootstrap" package (so a single package to import and bootstrap with settings or something like that) would be a good addition anyway.
Could this be moved to a discussion thread instead? I personally don't think this is worth the effort and see very few upsides (if any?), but pros and cons can be discussed more granularly there in comment threads.
What is this issue about?
Gutenberg could be released as a single
@wordpress/gutenberg
package instead of 40+ packages. It’s an idea @dmsnell had and shared with me recently.What are the upsides?
Here’s what it would do:
@wordpress
dependencies. This includes Gutenberg releases and WordPress core merges. It would be impossible to run into problems with mismatched packages versions.tsconfig.json
files and problematic cross-package dependenciespackage.json
files andbuild
,build-module
, andbuild-style
setupAnecdotally, maintaining wordpress-playground got significantly easier after I migrated the monorepo from four internal packages to a single repository-wide package.
What are the downsides?
First, let me flip the question – what value is added by having 40+ packages? Or, as @dmsnell phrased it:
I've heard the following concerns so far:
The existing packages wouldn’t disappear from
npm
. They would get deprecated. Every package would ship one final release where every relevant member would get re-exported from@wordpress/gutenberg
.A single package would make it even easier. Figuring out which of the 40+ packages are needed is a hit-and-miss trial-and-error process.
The bundler would take care of that automatically with tree shaking. The package consumer would import the bits they are interested in, and the bundler would remove all the irrelevant code. If two entrypoints require the same package, the bundler would move it to a separate chunk to avoid downloading the same code twice.
A single package could still produce the same set of JavaScript files as the existing 40+ packages.
This can continue to work in the same way. If necessary, a webpack plugin would take care of that. There could also be a new and explicit
registerStore()
utility.The logical split and the directory structure would remain exactly the same as it is now. Only the
package.json
files would be gone. As a side note, Gutenberg's separation of concerns isn't nearly perfect despite the use of packages.What are the alternatives?
@wordpress/bootstrap
) that would re-export everything from the existing 40+ packages. That would be a win for package consumers.Both would improve some things, but would not address the complexity related to Lerna, version management, and
__experimental
exports discussed earlier.What do you think?
CC @youknowriad @gziolo @mcsf @noisysocks @priethor @aristath @peterwilsoncc @carolinan @tellthemachines @talldan @georgeh @artemiomorales @kevin940726 @ramonjd @andrewserong @ellatrix