WordPress / gutenberg

The Block Editor project for WordPress and beyond. Plugin is available from the official repository.
https://wordpress.org/gutenberg/
Other
10.36k stars 4.14k forks source link

What if Gutenberg was released as a single npm package? #46679

Closed adamziel closed 1 year ago

adamziel commented 1 year ago

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:

Anecdotally, 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:

For all the slowdown, complexity, obstruction, confusion, incompatible-looking version numbers, hard-to-figure-out dependencies, undocumented relationships, and institutional knowledge required to keep the system moving, what are we getting?

I've heard the following concerns so far:

Switching to a single package will break the existing plugins relying on separate packages

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.

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.

How would e.g. the site editor not have JavaScript to do with the widgets editor? A lot of first and third pages rely on smaller bits like “components”, so we need a way to bundle wp-components or wp-data based on that single package.

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.

In WordPress, we talk about scripts and global variables and not about packages. These things shouldn’t change

A single package could still produce the same set of JavaScript files as the existing 40+ packages.

Some of the modules have side effects. For instance, if you import the block-editor package, it will register the store automatically.

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.

Packages allow a logical code split and force the developers to think about what should depend on what

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?

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

peterwilsoncc commented 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.

youknowriad commented 1 year ago

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:

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...)

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.

Requirements

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)

Proposed solution

The proposed solution is basically to just merge all the packages inside one using folders, so:

If I'm understand properly, we just publish a single package to npm instead of publishing multiple ones.

Promises

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.

Pros & cons


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.

mtias commented 1 year ago

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.