plone / Products.CMFPlone

The core of the Plone content management system
https://plone.org
GNU General Public License v2.0
254 stars 191 forks source link

Mockup and resource registry redone #3211

Closed thet closed 2 years ago

thet commented 4 years ago

PLIP (Plone Improvement Proposal)

Responsible Persons

Proposer: Johannes Raggam

Seconder: Maik Derstappen

Abstract

This PLIP is about reworking Mockup to be based on up-to-date JavaScript standards and to simplify the resource registry.

Mockup will be based on Patternslib version 4 and integrate many concepts from there:

The simplification of the resource registry is largely based on the removal of RequireJS:

Mockup and the resource registry should get much easier to understand and to work with, more up-to-date and match developer expectations better.

Additionally we want to advertise the development of new JavaScript functionality as web components but any JavaScript framework should be compatible with Plone.

Motivation

Mockup was once and avant garde answer to the growing problem of managing JavaScript in Plone. Based on Patternslib it offered clear and defined dependencies via RequireJS, a JavaScript testing framework and a flexible build system incorporating the resource registry with through the web compilation and the possiblity to overwrite specific resources with a custom implementation. Other technologies like web components or reactive JavaScript frameworks were not available or not an option back then.

Now, Mockup lags years behind current JavaScript development. It's no big fun to develop for Mockup (although there are nice parts, like live reloading in Plone without having to build bundles). Mockup depends on some long outdated and inactive libraries - the most important here is RequireJS. RequireJS was the corner stone to solve the dependency problem with JavaScript in Plone 4. But meanwhile it prevents us from using modern JavaScript features. There are also other outdated canditates: the complex integration of the Grunt build framework, the use of React 0.10 for our documentation framework, the integration of the unmaintained jquery.drag/drop which holds us on jQuery 1.x (there is a seperate PR for fixing this) - and so on.

The resource registry is also complex and hard to understand. The through the web compilation (actully client side compilation on the web browser) works like magic but creates a hard dependency on RequireJS and a specific version of the LESS compiler. This bounds us to a complex development workflow and prevents developers to make their own choices regarding JavaScript libraries, ECMAScript versions and build frameworks.

For good reasons we Plone community failed get developers on board helping out in the JavaScript area of Plone.

This PLIP aims to fix this situation by a number of actions outlined below.

Assumptions

1) In Plone 6, we still use Mockup and the resource registry.

Proposal & Implementation

Mockup will be completely restructured and based on the concepts of Patternslib 4:

For the resource registry the changes will be:

Caching URL improvements:

For integrators we encourage to write new JavaScript functionality wether as Patternslib Pattern or as a "web component" or "custom element". Bobtemplates.plone shows an example how to create a web component based on "Svelte", which has a low footprint and uses a standards-compliant web component approach. However, the framework is not important as anything can be used.

Deliverables

Risks

Participants

Resources

Mockup ES6 rewrite PR (WIP): https://github.com/plone/mockup/pull/1025 Patternslib ES6 (production ready): https://github.com/patternslib/Patterns Simple example pattern using Webpack, ES6, dynamic imports, Jest: https://github.com/Patternslib/pat-sortable-table/blob/master/src/pat-sortable-table.js

/cc @plone/framework-team

frapell commented 4 years ago

Having to do a Javascript bugfix with the existing resource registry is a complete nightmare... Huge +1 from me here

tisto commented 3 years ago

I am generally +1 on the intention of this PLIP. Though, we have to take into account that this completely breaks the add-on architecture of Plone as we know it. We were always reluctant to do this in the past because it is a huge step and a complete paradigm change.

How do you plan to keep the add-ons control panel with this new approach? You can not go to the add-ons control panel in Plone and install an add-on that has JS/CSS. You will always have to do a separate compile step and you need all the JS tooling properly installed for this. How do you plan to handle this? The add-ons control panel would give the user a false promise of being able to install an add-on product TTW, which won't be possible any longer. Are we going to remove TTW add-ons? Do we build a new JS package infrastructure? How do you plan to make JS packages depend on each other? How do you plan to do optimize the bundle when you have lots of add-ons installed?

I am playing devil's advocate here (mainly because the questions above are the challenges we had to solve in Volto). Though, this loss of one of Plone's core features was one of the main reasons we started with Plone-Angular/Plone-React/etc. We could not imagine doing such a fundamental break in Plone itself. And if the user has to install the complete JS toolchain and run webpack for each new add-on anyways, you can as well go all the way

If we talk about going in what I believe is the right direction, we should also talk about the long-term implications of this PLIP. Say we do this painful first step, the next step that makes sense is to make patternslib rely on Plone REST API, then make patternslib use a new modern JS framework (Rok wanted to rewrite mockup in React even before Plone 5 was released) and replace to old cruft. React is already in core and it basically won the JS framework war. Using something else at this point and not re-using Volto/React components would be just nuts (sorry for being blunt here). At this point we will have a wild mix of backend and frontend templates which is hellish (I saw many companies and people trying this and they failed 100% of the time). Then you want a clean separation between frontend and backend (which Plone REST API provides) at some point...

We went through this entire learning and decisions making process 3-5 years ago. We did all the mistakes, we tried way too many frameworks to end up with Volto. If this PLIP gets into core and we continue to work successfully on this, we either end up with a second Volto or with Plone classic becoming more and more what Volto already is. In any case, we will split the limited resources that we have working towards the same goal, to have a well maintained JS stack for Plone.

As said, I am not opposed to this PLIP. I am strongly against bringing yet another JS framework into core. Though, I hate to see that we put effort into solving a problem that we had not too long time ago and that we already successfully solved with Volto.

One other thing to consider: we had to come up with a complete new add-ons architecture for Volto and we still have to figure out how to make this work in Plone 6. If we will have two JS add-on architectures and JS build systems for Plone 6 this will confuse the hell out of people.

jensens commented 3 years ago

How do you plan to keep the add-ons control panel with this new approach? You can not go to the add-ons control panel in Plone and install an add-on that has JS/CSS.

As I understand @thet this is still perfectly possible, and much easier than before.

tisto commented 3 years ago

Do you plan to use @datakurre's approach to querying the Plone resources and bundle them externally?

thet commented 3 years ago

@tisto thanks for raising these questions!

I think most of add on JavaScript might just be installable as simple, separately packaged bundles. That should not make much problems as long as they do not have dependencies to Mockup's dependencies (except jQuery which is still available in the global namespace). If so, we might end up delivering multiple versions of these dependencies. To solve that, integrators would need to create a custom JavaScript bundle with all the necessary dependencies - something I see a neccessity for any mid-sized project anyways, also with our current infrastructure in Plone 5.x. So, quick add-on installations should just work because they can register their compiled bundles with all their dependencies - the drawback here will be at worst non-optimized resource delivery with multiple versions of the same libraries.

Regarding moving to plone.restapi: a big +1 for all of Mockup's API calls. For Patternslib this is not so relevant - currently we only use the Patternslib core to register patterns. At a recent talk with @MrTango we agreed that we might not move to plone.restapi right away to keep up the backwards compatibility - there might be some customized endpoints used in projects. However, using plone.restapi instead of custom endpoints is on my wishlist too.

Regarding a rewrite to use a modern JS framework: Also +1 but not right away. The first goal would be to modernize the whole codebase. Then we can explore possibilities to integrate other frameworks - even much easier when we switched to ES6+ and removed RequireJS.

Reusing Volto components: definetly a good idea, especially since Volto has a lot of components which Mockup also offers but in a much better quality. I'd love to have Volto more modularized and be able to just import individual components without having to rewrite Mockup in React. The other way around would also be a very interesting option: moving towards Web Components and reusing thosre in Mockup and Volto at the same time (Mockup then might even be obsolete).

And no, I don't see the resource registry involved in compiling bundles, as @datakurre did for his Webpack projects. Actually all resources should be cleared from the resource registry - IMO we do and should not need them anymore. If one wants to create a optimized bundle it would be necessary to set up a JavaScript project, add all dependencies to package.json and create a bundle there. We need a template to make it easy to bootstrap such a project.

sneridagh commented 3 years ago

I have good news for (all of) you. This PLIP is already implemented, it's called Volto: https://github.com/plone/volto/

There is a whole track (Track 1) at the Plone conference about it, so you all can get updated with all the new things the Volto community has implemented there, and the plans for the future.

https://2020.ploneconf.org/schedule

Oh, one more thing, as far as I know Volto is Plone 6.

jensens commented 3 years ago

@sneridagh that does not help, even if you put many blood and tears into Volto. We need Plone 6 Core still, we can not all switch immediately to Plone 6 Volto. Do not get me wrong, I really like Volto. But Plone 6 Core with server side template based rendering will exist for a while.

Oh, one more thing, as far as I know Volto is Plone 6.

So no, Plone 6 is both: Core and Volto.

On install there must be a choice what to install. I know the marketing team hates that, the Volto team either. But we can not ignore the reality of Plone users, companies, organisations, developler, ...

sneridagh commented 3 years ago

That's not the issue, there can't be two modern JS stories in Plone. Full stop.

Svelte? Add yet another different web technology to the stack? Web components what? Really? No, thanks. We worked our asses off in Volto to improve the approachability to Plone for now do this.

When you start to work on it, you'll get into the same pitfalls and will solve the same issues we already solved. You'll end up doing exactly the same we did for Volto.

By doing this, you will confuse people, and valuable resources will be spent in re-writing what it's already done. That also hurts.

Sorry, overlook them and ignore (and continue doing so) them hurts me, and all the Volto community (and yes, there is one, a healthy one).

thet commented 3 years ago

@sneridagh Plone 6 will still ship with the page templates rendered on the server. There is also the barceloneta-lts project for modernizing the UI. And for that we need (IMO) an updated JavaScript stack.

This PLIP is just about modernizing the existing JavaScript stack and is not meant to compete with Volto.

Regarding Svelte, Web Components etc: Just ignore that. Whatever developers use to add JS functionality to "classic" Plone is up to them.

Still, I'd really love to see Volto modularized in a way that individual components like folder content browser can be reused outside of Volto. I did not have time to explore that, but this is on my list.

MrTango commented 3 years ago

@sneridagh I understand your point, but I don't think it's all black or white. And these discussion of everybody should work on the same project, in the same way is as old as open source is. The reality is, we have to worlds here and the gab is not small. Not everyone can just jump on the Volto train right now. My vision of this is, to update and fix the existing Mockup/patterns and where needed make it possible to be creative and create new better once.

The goal is, what ever get's in there has to be really reusable. Svelte components and especially web components (custom elements) are ideal for this job. They work pretty much everywhere (Plone, static HTML, Patternslib, JS Frameworks) and also be able to use Redux, like Volto.

The way I see it, this will bring the core Plone and Volto closer together and make it easier for people to switch at some point.

It might not be obvious right away, but Volto can benefit from sharing components with existing Plone UI.

But back to the main points of this PLIP:

I know, nothing in this list is concerning, when one is using the Volto frontend, but this is not an option for everybody and not for every project at this time.

I feel comfortable with backend and frontend development, but many existing Plone developer are not and for them the existing Plone core does a better job. Let's please not forget them and force people to abandon the existing Stack. They both can exist and hopefully start sharing more together than the backend. Volto will grow and so will the developer base on the frontend side, but this is not going over night.

fulv commented 3 years ago

LTS is one of the suggested names for the old-style Plone stack with server-side rendered templates, aka Plone 6 Classic / Plone 6 Core / Plone 6 LTS.

Anyway, in the spirit of LTS I think it would make little sense to force people to rewrite any add-on code whatsoever. Any backwards incompatibility means we are forcing someone to decide whether to spend their dev money on an upgrade vs a switch to a different platform. We simply cannot afford to lose a single Plone installation. So don't give them a reason to leave.

I appreciate all of the ideas in this PLIP, and I know how painful the resource registry and the JS experience is. So I welcome any and all improvements.

But, again, in the spirit of LTS, I suggest making only the improvements that don't break backwards compatibility. The resulting PLIP might be much uglier and possibly even harder when done with this goal in mind, but a bird in the hand is worth two in the bush.

jensens commented 3 years ago
MrTango commented 3 years ago

+1 for not breaking with add-on's. There is no intention of that, we will do what we can to make it as seamless as possible for add-on developers. The whole idea is to keep as much existing devs in Plone land and make it easy enough for new people to contribute. The ES6 changes are for Mockup and are the way to go to replace RequireJS.

MrTango commented 3 years ago

And to make it clear, there will be no other JS framework comming into core. custom-elements are not a framework but an existing web standard which is supported by all modern browsers. How you create such custom elements is up to you. One way is from cratch, one is to use libraries like LIT-Element and another is to use compiler like Stencil and Svelte, which produces JS at the end. So there is no framework at the end and also there is no runtime of a framework.

thet commented 3 years ago

Removing RequireJS will break backwards compatibility for add-ons which depend on RequireJS. I'd like to know if there are many. I have the impression that there are only a handful of add-ons which have a pattern defined. Those can be easily rewritten to ES6 module imports as part of this PLIP. However, this ES6 module import rewrite is easy.

jensens commented 3 years ago

@thet and add-ons with JavaScript not implemented as pattern are not affected. Except if they are using RequireJS?

thet commented 3 years ago

@jensens yes, exactly. "legacy" bundles as we called them (no compile flag set, no requirejs) should still work as before.

bloodbare commented 3 years ago

@sneridagh you can always be Guillotina 7 Super power UI ;)

thet commented 3 years ago

Added a note about using Patterns from Patternslib instead of Mockup where possible as an optional task. First step would of course to be to bring Mockup to ES6 and the test suite green.

datakurre commented 3 years ago

@thet @MrTango You are such heros for working on this! Just a few thoughts from my experience on building all of our Plone 5 themes with webpack to possibly keep in mind:

MrTango commented 3 years ago

Since we had some confusions in todays FrameWorkTeam meeting, about what the change with the Resource Registry and the removal of requireJS means for Add-on's.

Add-on's will be installable as before, no change there. There will be no big Webpack to rule them all. We will use webpack to build the plone-core bundle which includes things like Mockup/Patterns and some core resources. This bundle will be registered in the Resource Registry as it is now.

Resouces for add-on's will be manged by the individual add-on. There is no dependency injection happening. Add-on's should build decent bundle sizes and thing about if they schould be loaded all the time or just in some situations. If an add-on has a bigger library it depends on, this should be dynamically (lazy) loaded when the code is used. This keeps the bundle sizes down and resourcs are only loaded when they are needed.

alecpm commented 3 years ago

Hi all, I hope I'm not speaking out of turn here. This seems to me like a very good approach, but I'd like to stress the importance of high quality comprehensive documentation for major infrastructural changes like this.

In my experience, one of the biggest issues with the Plone 5 Resource Registry was a lack of documentation of how to use it either as an add-on developer or a site integrator. The initial documentation was largely inadequate until a while after release, and it's still far from comprehensive. For example, there's still no documentation indicating best practices for writing/updating an add-on to support multiple versions of Plone with different RR schemes. I believe that sort of thing is very important if we want people to e.g. update add-ons to properly support newer versions of Plone.

thet commented 3 years ago

From the discussion at the open space at the Plone conference 2020:

Adding JavaScript code via addons.

Adding JavaScript code via addons will be easier in a way that we allow to add bundles by providing them as static resources and adding a registry entry for that. The simplification to the current situation will be:

As far as the optimization of extra bundles added via addons goes, we have these options:

1) Not optimizing bundles at all. If an integrator doesn't know how to optimize it might not be that of an important site anyways and optimization not worth the effort.

2) Running webpack after buildout. Doing some basic automatic optimization as part of the Plone build step.

3) Webpack Module Federation. Allowing bundles to depend on Code from other bundles with this new Webpack 5 feature. Jupyter Notebooks is doing the same. This is an intersting option where we want to invest some time in to get some experience.

Notheworthy mention: CalmJS (https://pypi.org/project/calmjs/) allows for dependencies managed in Python code (setup.py/cfg). As this is much like Funstatic a solution fun off the standard we probably do not follow it.

Whatever we do, we need to make it simpler. We want to reduce complexity and probide a well documented, painless, easy, sane and reproducable way of managing JavaScript in Plone.

datakurre commented 3 years ago

:100:

IMO, an after all the discussions, considering the community response to sophisticated Plone 5 resource management, returning back to "just bundles" is the only right way to do. Hopefully there is sane way add-ons to support this in backwards compatible way, when required. Conditional records should help https://pypi.org/project/plone.app.registry/#conditional-records

I'm looking forward for Webpack module federation to solve most of the issues with add-on families similar to eea-packages and allow them to avoid duplicating dependencies in individual bundles.

The complete Webpack approach presented in plonetheme.webpacktemplate will become a bit harder now that the source files are now longer in the registry (or packed with the add-on packages), but must most probably be fetched from their original sources or npmjs. That was my first approach years ago, but back then it was a nightmare to maintain compared to fetching everything from Plone registry. But I'm sure that this will become much easier now that Mockup itself is modernized. So, once I'd updated plonetheme.webpacktemplate for Plone 6, also this use case is covered.

Rudd-O commented 2 years ago

PLIP looks good. Only thing I would love to see is documentation for addon writers to port their addons to the new tech.

MrTango commented 2 years ago

Resource Registry

We have a new much simpler resource registry (RG) which is basically a form not a JS based UI as before, so it will not break when you change things in the RG. You only register compiled bundles, this is very close to as it was possible before. There will be no more individual resources, as we don't support bundling in the browser. What you use to create your resource bundles is up to you. It can be plain CSS/JS or compiled by Rollup or Webpack & co.

Mockup / Patterslib Patterns

All resources are standard ES-modules and can be use in you own project Webpack & co and be further optimized. Patternslib as already available as an npm package, this could be done with Mockup too, to make it easier to reuse it in projects. In general the components are more lightweight and easier to customize via CSS overrides, since they are no longer scoped. Most big resources are fractured out and loaded dynamically when they actually needed.

Better Add-on's support

Besides that the registration of resources will be simpler in general, you will have some advanced options to use JS-libraries, which you can use if you want. Things like Mockup, Patternslib and potentially others can be shared by add-on's via Webpack Module Federation. For an add-on this means, with a bit extra config, you can share a library with other participants in Plone and avoid double loading and conflicts. But all this is opt-in, so if you don't care, nobody will bother you.

The migration of add-on's will be fairly simple. If it uses so called legacy (externally compiled) bundles, there is nothing to do. If you where so brave and wrapped the resources with requireJS, this can be removed. All you need is a simple bundle registration of whatever CSS/JS you have. For add-on's which where using Mockup/Patternslib via RequireJS, this needs to be changed to a Webpack Module Federation style, which will be documented soon.

Status

We only have some small bugs in pat-relateditems and some unit tests to fix, the rest in already in a good shape. The Webpack-Module-Federation will be tested with some add-on's in the coming weeks and the whole story documented.

ale-rt commented 2 years ago

This was discussed in the @plone/framework-team mailing list and nobody spoke against. Personally I would consider this one approved and also not really @plone/framework-team material given that is geared towards the frontend and the framework team decided to limit its activity to the backend code.

Anyway it is still good to have a PLIP, I think.

mauritsvanrees commented 2 years ago

I have tested it, from the point of view of a site admin or editor, and a bit as add-on writer. I did not look at how things are implemented in the code. Tested on Mac in Firefox.

Some review notes:

I have created an issue for the first point. I can create separate issues for the others if wanted. Where? In the Products.CMFPlone issue tracker?

The migration problem strikes me as the main one that would need to be tackled soon, otherwise a migrated site basically does not work. Being able to move items in folder contents would be welcome too. The other items seem minor and could be fixed later.

I did not notice problems in the Site Setup or TinyMCE. Everything works the same or better. I did not do in depth testing though.

I did not try adding an add-on with some css and javascript. But it looks really easy. You point to one javascript and one css file of the add-on. Maybe specify one or more other bundles like jquery as depends, which I guess simply means that those are loaded first. And that is it. No bundling or other combining. I think you get more css and js files than in Plone 5.2, exactly because nothing is combined anymore. Or is some bundling still happening, simply by concatenating files? Anyway, this seems a small price to pay for sanity restored with an up to date javascript story. Marvelous work!

Actually, since it sounds easy, let me create an add-on with plonecli create addon collective.foo. I manually created a foo.js and foo.css, where the javascript file uses jQuery ($). I added this resource.xml:

  <records prefix="plone.bundles/collective.foo" interface='Products.CMFPlone.interfaces.IBundleRegistry'>
    <value key="enabled">True</value>
    <value key="jscompilation">++plone++collective.foo/foo.js</value>
    <value key="csscompilation">++plone++collective.foo/foo.css</value>
    <depends>jquery</depends>
    <value key="load_async">False</value>
    <value key="load_defer">False</value>
  </records>

I activated the add-on, and both my javascript and css worked. Without jquery in the depends it fails because $ is not defined. So it works as I expect it to, and is easy. Great! Two minor things:

I added collective.easyform 3.0.5, which does not yet have any changes for Plone 6. The registry.xml only tries to load the css, and this works unchanged. The javascript is not handled via the resource registries, but is only loaded on the pages for editing fields and actions. This fails with AttributeError: ++resource++schemaeditor.js. So easyform is a special package which needs some updates for Plone 6. There is a PR with fixes so I try it. Then it works. I now see that easyform does not even have own javascript, except a small bit inline. And the most important thing that makes it work, is that it uses pat-schemaeditor.

On the network tab I see some javascript chunks getting loaded. I don't know if that is the module federation or something else. But it seems to work.

Summary: it is looking pretty good!

yurj commented 2 years ago
  • On a new site, this works fine. Comparing the Resource Registry control panels, the migrated site only has the bootstrap-js and plone bundles, so it is missing jquery, plone-legacy and plone-logged-in.

It happened to me too, If you re-run the last staticresources import step and cmfplone one, jquery bundle pops up. Seems like some upgrade steps add/erase things, and something runs not in the right order.

jensens commented 2 years ago

No bundling or other combining. I think you get more css and js files than in Plone 5.2, exactly because nothing is combined anymore. Or is some bundling still happening, simply by concatenating files?

This is by intend.

mauritsvanrees commented 2 years ago

@thet has merged everything. Thank you Johannes, @MrTango, @petschki, @jensens, @agitator, @pbauer and lots more people! [Feel free to edit for adding more people, or add comments below. Listing names is dangerous...]

mauritsvanrees commented 2 years ago

Some remaining issues I saw:

ale-rt commented 2 years ago

Also I would like to see some documentation on this one and if possible a demo add on, something similar to https://github.com/collective/example.p4p5. It does not need top work on both p5 and p6 (though it would be a nice plus) but at least it should be really clear how to wire up custom css + javascript in Plone 6 add on.

mauritsvanrees commented 2 years ago

This can be closed, right? A documentation issue is still open: https://github.com/plone/documentation/issues/1142