Problem/Motivation

Discovered in #3272732: Drupal 10 & CKEditor 5 readiness, while porting Entity Embed.

In #3272732: Drupal 10 & CKEditor 5 readiness, I recommended using a deriver instead of a hook that alters CKEditor 5 plugin definitions to de facto derive multiple plugins from a single base definition.

But it looks that due to \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::getDiscovery() overriding \Drupal\Core\Plugin\DefaultPluginManager::getDiscovery(), we do not get the default derivative support 🙈

Steps to reproduce

Specify a deriver, observe it does not get called!

Proposed resolution

Add support for derivers.

Remaining tasks

  1. Tests
  2. Fix
  3. Review

User interface changes

None.

API changes

None.

Data model changes

None.

Release notes snippet

TBD

CommentFileSizeAuthor
#43 3313473-43.patch28.72 KBWim Leers
#43 interdiff.txt7.05 KBWim Leers
#42 3313473-42.patch28.35 KBWim Leers
#42 interdiff.txt3.84 KBWim Leers
#40 3313473-40.patch28.46 KBWim Leers
#40 interdiff.txt10.05 KBWim Leers
#35 3313473-35.patch27.18 KBWim Leers
#35 interdiff.txt10.02 KBWim Leers
#24 3313473-24.patch26.64 KBWim Leers
#24 interdiff.txt3.13 KBWim Leers
#23 3313473-23.patch24.54 KBWim Leers
#23 interdiff.txt1.21 KBWim Leers
#16 3313473-16.patch24.64 KBWim Leers
#16 interdiff.txt2.41 KBWim Leers
#14 3313473-14.patch27.06 KBWim Leers
#14 interdiff.txt7.57 KBWim Leers
#13 3313473-13.patch24.67 KBWim Leers
#13 interdiff.txt6.09 KBWim Leers
#12 3313473-12.patch21.86 KBWim Leers
#12 interdiff.txt5.85 KBWim Leers
#10 3313473-10-tests-only.patch15.83 KBWim Leers
#10 interdiff.txt9.21 KBWim Leers
#7 3313473-7-tests-only.patch10.83 KBWim Leers
#7 interdiff.txt2.13 KBWim Leers
#6 3313473-6-tests-only.patch8.58 KBWim Leers
#6 interdiff.txt1.39 KBWim Leers
#5 3313473-5-tests-only.patch8.84 KBWim Leers
#5 interdiff.txt1.46 KBWim Leers
#4 3313473-4-tests-only.patch9.15 KBWim Leers
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

Wim Leers created an issue. See original summary.

Wim Leers’s picture

Assigned: webchick » Wim Leers

Oops 🤣

Crediting @balintpekker! :)

Wim Leers’s picture

Status: Active » Needs review
FileSize
9.15 KB
Wim Leers’s picture

PHPStorm's automatic reformatting caused that … 😬

Wim Leers’s picture

Wim Leers’s picture

I forgot how painfully long this takes. Let's run only CKE5's test suite.

The last submitted patch, 6: 3313473-6-tests-only.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 7: 3313473-7-tests-only.patch, failed testing. View results

Wim Leers’s picture

Issue summary: View changes
Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
9.21 KB
15.83 KB

Expand test coverage.

Also: run only CKE5's kernel tests, not all.

Status: Needs review » Needs work

The last submitted patch, 10: 3313473-10-tests-only.patch, failed testing. View results

Wim Leers’s picture

Issue summary: View changes
Status: Needs work » Needs review
FileSize
5.85 KB
21.86 KB

And … fix!

CR also created: https://www.drupal.org/node/3313821

Wim Leers’s picture

Assigned: Wim Leers » Unassigned
FileSize
6.09 KB
24.67 KB

Stricter test coverage.

Ready for review now!

Wim Leers’s picture

Last but not least: test coverage to prove that this works fine for PHP class annotation-based plugin definitions too!

Wim Leers’s picture

Pointing out some key changes to help review this:

  1. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginManager.php
    @@ -61,6 +62,7 @@ protected function getDiscovery() {
    +      $discovery = new ContainerDerivativeDiscoveryDecorator($discovery);
    

    This is the key change, but changes were necessary elsewhere too: in the actual production code to A) allow defining a deriver, in the test infrastructure B) allow this to be tested thoroughly.

  2. +++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
    @@ -78,38 +82,44 @@ protected function setUp(): void {
    +  private function mockModuleInVfs(string $module_name, string $yaml, array $additional_files = []): ContainerInterface {
    

    This extracts a helper method from ::testInvalidPluginDefinitions(), so that it can be reused in the new test method.

  3. +++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
    @@ -123,12 +133,15 @@ public function testInvalidPluginDefinitions(string $yaml, ?string $expected_mes
    +      'container.namespaces' => [
    +        "Drupal\\$module_name" => vfsStream::url("root/$site_directory/modules/$module_name/src"),
    +      ] + $this->container->getParameter('container.namespaces'),
         ] + $this->container->getParameterBag()->all()));
    

    This is a necessary addition to allow \Drupal\Component\Annotation\Plugin\Discovery\AnnotatedClassDiscovery::getDefinitions() to find PHP class annotation-based plugin definitions.

Wim Leers’s picture

Status: Needs review » Needs work

The last submitted patch, 16: 3313473-16.patch, failed testing. View results

Wim Leers’s picture

Status: Needs work » Needs review
1) Drupal\Tests\system\Functional\UpdateSystem\UpdatePathTestBaseFilledTest::testUpdatedSite
GuzzleHttp\Exception\ConnectException: cURL error 52: Empty reply from server (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for http://php-apache-jenkins-drupal-patches-151554/subdirectory/update.php/start?id=14&op=do_nojs

→ definitely an unrelated failure 🙃

Status: Needs review » Needs work

The last submitted patch, 16: 3313473-16.patch, failed testing. View results

Wim Leers’s picture

Status: Needs work » Needs review

Eh …

1) Drupal\Tests\layout_builder\Functional\LayoutBuilderFormModeTest::testDiscardValidation
Drupal\Core\Extension\Exception\UnknownExtensionException: Unknown themes: classy.

Apparently #3165010: Using the layout builder discard changes button should ignore any input and skip validation temporarily broke HEAD, it's been fixed. Re-testing again.

smustgrave’s picture

+1 for RTBC. Code looks good to me but would like a 2nd opinion too.

mglaman’s picture

Some nits. I didn't pull down to fully read. But the plugin constructor changes seem very different than other code. But that's probably because CKE5 plugins are very different.

  1. +++ b/core/modules/ckeditor5/src/Annotation/DrupalAspectsOfCKEditor5Plugin.php
    @@ -42,6 +42,15 @@ class DrupalAspectsOfCKEditor5Plugin extends Plugin {
    +  public $deriver = NULL;
    

    nit: it's technically already null. I think this would only be needed if we used typed properties.

  2. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
    @@ -304,6 +339,28 @@ public function setClass($class) {
    +    // possible that this plugin definition is a partial/incomplete one, and the
    +    // default from the annotation is only applied at the time that
    

    "the time that" that what?

  3. +++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
    @@ -1519,4 +1546,325 @@ public function testExternalLinkAutomaticLinkDecoratorDisallowed(): void {
    +  public function testDerivedPluginDefinitions(string $yaml, ?string $expected_message, array $additional_files = [], ?array $expected_derived_plugin_definitions = NULL): void {
    

    isn't it PHP code and not YAML?

Wim Leers’s picture

Thanks for the review!

  1. It is indeed already technically NULL. But … this particular key-value pair in the annotation explicitly allows NULL as a valid value, as well as any string. Hence also @var string|null, unlike for example for protected $label, which is NULL by default but is required.

    This is used by \Drupal\Component\Annotation\Doctrine\DocParser::collectAnnotationMetadata(), which interprets @var.

    Precedents: \Drupal\media\Annotation\MediaSource::$thumbnail_alt_metadata_attribute and \Drupal\media\Annotation\MediaSource::$thumbnail_title_metadata_attribute.

  2. 😬 Hard to recall after introducing this 3 weeks ago in #12 … did some debugging, figured it out again 👍
  3. No, the first argument for each of the test cases is YAML; the PHP code lives under $additional_files. Same thing for the pre-existing providerTestInvalidPluginDefinitions() + testInvalidPluginDefinitions().
Wim Leers’s picture

Paired with @balintpekker on #3272732: Drupal 10 & CKEditor 5 readiness, to figure out why he couldn't get his deriver to work. Turns out he was returning an array as the CKEditor 5 plugin definition in his deriver, instead of a CKEditor5PluginDefinition instance.

The deriver infrastructure introduced here is working perfectly fine for that issue now (see https://git.drupalcode.org/project/entity_embed/-/merge_requests/12/diff...) … except that this clearly points to a low-hanging fruit DX improvement! 🍎

So let's do that :)

All we need to fix that is apply the same approach as \Drupal\Core\Layout\LayoutPluginManager::processDefinition 🤓

Status: Needs review » Needs work

The last submitted patch, 24: 3313473-24.patch, failed testing. View results

Wim Leers’s picture

Status: Needs work » Needs review

The 1) Drupal\Tests\datetime\Functional\DateTimeWidgetTest::testDateonlyDefaultValue failure is 1000% unrelated — I wonder if this is some DST thing? 🤯

Wim Leers’s picture

Wim Leers’s picture

#26 was for the 10.1.x result.

On the 10.0.x test result, we get:

1) Drupal\Tests\media_library\FunctionalJavascript\WidgetViewsTest::testWidgetViews

… yet another random JS test fail… created #3317378: [random test failure] Drupal\Tests\media_library\FunctionalJavascript\WidgetViewsTest::testWidgetViews random fail.

smustgrave’s picture

Status: Needs review » Reviewed & tested by the community

Going off the 10.1.x pass

Looks like the changes in #22 were address or answered.

RTBC to me!

Excited to see ckeditor5 come along so much in the last few months!

mglaman’s picture

Turns out he was returning an array as the CKEditor 5 plugin definition in his deriver, instead of a CKEditor5PluginDefinition instance.

Interesting. Usually the Plugin API system expects/assumes derivers return an array of definitions, not the object definitions themselves. And that's an entirely different discussion than this. The fixes look good to me 👍

Wim Leers’s picture

#30: that's the usual, yes, but not a requirement. LayoutPluginManager does the same.

Basically, by not using blobs/arrays of doom but typed objects, we can offer a vastly better DX.

tim.plunkett’s picture

+++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
@@ -51,11 +61,36 @@ public function __construct(array $definition) {
+    catch (InvalidPluginDefinitionException $e) {
+      // If this exception is thrown for a CKEditor 5 plugin definition whose ID
+      // matches a previously seen base plugin ID, it means the deriver did not
+      // generate a valid plugin definition. Re-throw the exception, but tweak
+      // the language for DX: clarify it is for a derived plugin definition.
+      if (!empty(static::$basePluginIds) && preg_match('/^(' . implode('|', static::$basePluginIds) . ').*/', $this->id)) {
+        throw new InvalidPluginDefinitionException($e->getPluginId(), str_replace('plugin definition', 'derived plugin definition', $e->getMessage()));
+      }
+
+      // Allow an invalid plugin definition only if it's yet to be derived.
+      if (isset($definition['drupal']['deriver']) && class_exists($definition['drupal']['deriver'])) {
+        // Pass on all information from the base definition, without validation;
+        // it will be validated later.
+        $this->ckeditor5 = $definition['ckeditor5'] ?? [];
+        $this->drupal = $definition['drupal'];
+        // Track which IDs should be considered base plugin IDs.
+        static::$basePluginIds[] = $this->id;
+        return;
+      }

I tried and failed to find another way to accomplish this. Ideally it would have been handled by the deriver itself, but I can't think of a way to do that. This is a safe and reasonable approach, so +1 for RTBC

Wim Leers’s picture

@effulgentsia asked in chat:

For https://www.drupal.org/project/drupal/issues/3313473#comment-14761626, it looks in the test coverage and change record, it's following the pattern of derivativeID = baseID . '_' . suffix, which then leads to a total pluginID of
{baseID}:{baseID}_{suffix}. Is that a pattern we use elsewhere? I'm not clear why this can't be simplified to derivativeID = suffix and total pluginID = {baseID}:{suffix}.

CKEditor 5 plugins are not allowed to use periods or colons in their plugin ID, because otherwise it becomes impossible to define config schema for them. This was done and enforced in #3215689: Rename CKEditor 5 plugin IDs to guarantee all plugins can be made configurable, where \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition::__construct() got this:

  public function __construct(array $definition) {
    $this->id = $id = $definition['id'];

    $expected_prefix = sprintf("%s_", $definition['provider']);
    if (strpos($id, $expected_prefix) !== 0) {
      throw new InvalidPluginDefinitionException($id, sprintf('The "%s" CKEditor 5 plugin definition must have a plugin ID that starts with "%s".', $id, $expected_prefix));
    }
…

This proves that the actually used plugin ID is not {baseID}:{baseID}_{suffix} but {baseID}_{suffix} — otherwise tests would've been failing. (Although to be fair, explicit checks that disallow . and : would be even better.)

The reason the {baseID}:{baseID}_{suffix} plugin ID exists at all is simply because it's the unused array key generated by \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator::encodePluginId() in \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator::getDerivatives() — they're really just arbitrary array keys to prevent conflict. The source of truth is in the generated derivative itself.

You can see this in:

+++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
@@ -1519,4 +1546,363 @@ public function testExternalLinkAutomaticLinkDecoratorDisallowed(): void {
+        'ckeditor5_derived_plugin_foo:ckeditor5_derived_plugin_foo_A' => [
...
+          'id' => 'ckeditor5_derived_plugin_foo_A',
...
+        'ckeditor5_derived_plugin_foo:ckeditor5_derived_plugin_foo_B' => [
...
+          'id' => 'ckeditor5_derived_plugin_foo_B',

These array keys are irrelevant/arbitrary, the 'id' key-value pairs matter, because they will be returned by \Drupal\Component\Plugin\Definition\PluginDefinitionInterface::id().

Several other derivers in core set an explicit derived plugin ID. IOW: it looks like this "plugin IDs can get out of sync" thing is an oversight in the plugin system. And it looks like this has been known for a while:

The only solution until those 2 issues are fixed would be to create a tweaked ContainerDerivativeDiscoveryDecorator, which overrides encodePluginId(). I'd rather see the plugin system get improved.

P.S.: once again unrelated random failures in #24 too, opened an issue for them: #3317515: Random fail in Drupal\Tests\quickedit\FunctionalJavascript\CKEditor5IntegrationTest::testArticleNode() on 9.4 and 9.5.

Wim Leers’s picture

Assigned: Unassigned » Wim Leers
Status: Reviewed & tested by the community » Needs work

I'm almost convinced by that argument, but what's the problem with colons in config schema? Can't you quote the key like in https://git.drupalcode.org/project/search_api/-/blob/8.x-1.x/config/sche...?

— @effulgentsia in chat.

WELL DAMN! Will have to address that then…

Wim Leers’s picture

This is way better, thanks so much @effulgentsia! 🤩

Wim Leers’s picture

Re-testing #35 on 9.5 — the only failure was fixed by #3317515-12: Random fail in Drupal\Tests\quickedit\FunctionalJavascript\CKEditor5IntegrationTest::testArticleNode() on 9.4 and 9.5, which got committed in the past 😄

There is a legitimate 9.4 failure — but we can deal with that later, let's first get this into the 3 most important branches 🤞

tim.plunkett’s picture

+++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
@@ -43,7 +53,9 @@ final class CKEditor5PluginDefinition extends PluginDefinition implements Plugin
+    $this->id = $id = !isset($definition['base_id'])
+      ? $definition['id']
+      : sprintf("%s:%s", $definition['base_id'], $definition['id']);

+++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
@@ -1519,4 +1546,367 @@ public function testExternalLinkAutomaticLinkDecoratorDisallowed(): void {
+      $definition['base_id'] = $base_plugin_definition->id();
+      $definition['id'] = $id;

This seems unnecessary when you consider how \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator::encodePluginId() is used. Idk that it makes sense for the plugin definition constructor to have any of this logic (for that matter, I'm not sure I like the pre-existing code in here either)

The recent changes are good, I'm just unsure if it's far enough

effulgentsia’s picture

Status: Needs review » Needs work

Right, I think related to #38, the problem is in:

+++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
@@ -1519,4 +1546,367 @@ public function testExternalLinkAutomaticLinkDecoratorDisallowed(): void {
+      $this->derivatives[$definition['id']] = new CKEditor5PluginDefinition($definition);

getDerivativeDefinitions() should return an array of arrays, not an array of objects. Turning the definition array into an object should only happen in CKEditor5Plugin::get(), after DerivativeDiscoveryDecorator has merged the derivative definition with the base definition. That way, the deriver doesn't need to deal with IDs at all (other than as the key for the array returned by getDerivativeDefinitions()), and the ID generated by DerivativeDiscoveryDecorator::encodePluginID() is what ends up getting into the merged $definition array passed to CKEditor5PluginDefinition::__construct().

Wim Leers’s picture

Status: Needs work » Needs review
FileSize
10.05 KB
28.46 KB

#38:
But encodePluginId() only sets the array key, it doesn't actually update the plugin definition. That's exactly the bug that #2458769: Derivative plugin definitions contain base plugin IDs instead of derivative IDs still needs to still solve. I'm happy to add a @todo for that? Done.

Idk that it makes sense for the plugin definition constructor to have any of this logic (for that matter, I'm not sure I like the pre-existing code in here either)

This statement of yours made things "click" for me! 😄

I've moved all of this logic into \Drupal\ckeditor5\Plugin\CKEditor5PluginManager::processDefinition() and … sure enough, that works fine! It even hugely simplifies the code you were concerned about (and I was too!) in #32 👍


#39:

getDerivativeDefinitions() should return an array of arrays, not an array of objects.

-1 — the $base_plugin_definition it receives is already a \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition instance. This is unavoidable because \Drupal\Component\Annotation\Plugin\Discovery\AnnotationBridgeDecorator::getDefinitions() explicitly calls <Plugin>::get(), and \Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator::getDerivatives() runs after that.

That means that

          // Use this definition as defaults if a plugin already defined
          // itself as this derivative.
          if ($derivative_id && isset($base_plugin_definitions[$plugin_id])) {
            $derivative_definition = $this->mergeDerivativeDefinition($base_plugin_definitions[$plugin_id], $derivative_definition);
          }

is never executed for CKEditor 5 plugin definitions. Nor do I actually even understand when that would ever get executed.

(Note that \Drupal\Core\Plugin\DefaultPluginManager::processDefinition() has a similar note about array-based definitions vs others.)

(Same thing for Layout plugins btw, see/put a breakpoint in \Drupal\Core\Layout\Annotation\Layout::get() and run \Drupal\Tests\Core\Layout\LayoutPluginManagerTest::testProcessDefinition() for example. For them too, that cited code block is never executed.)

(In fact, the only way I was able to get that code to execute is by running \Drupal\KernelTests\Core\Plugin\DerivativeTest::testDerivativeDecorator, and in doing so I discovered it merges the definitions incorrectly: the derivative's label is overwritten by the base definition's label …)


IOW: #38 has been addressed, #39 has not, because I don't see how I could 😅

tim.plunkett’s picture

  1. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
    @@ -40,22 +41,17 @@ final class CKEditor5PluginDefinition extends PluginDefinition implements Plugin
       public function __construct(array $definition) {
    ...
    +    foreach ($definition as $property => $value) {
    +      if (property_exists($this, $property)) {
    +        $this->{$property} = $value;
    +      }
    +      else {
    +        throw new \InvalidArgumentException(sprintf('Property %s with value %s does not exist on %s.', $property, $value, __CLASS__));
    +      }
         }
    

    I *LOVE* this new constructor. I'm so so glad we ended up resolving my original concerns after I gave up on them :D :D

  2. +++ b/core/modules/ckeditor5/src/Plugin/CKEditor5PluginDefinition.php
    @@ -304,6 +306,31 @@ public function setClass($class) {
    +    // @see \Drupal\ckeditor5\Plugin\CKEditor5PluginDefinition::__construct()
    

    I think this needs to point to CKEditor5PluginManager::processDefinition() now

  3. +++ b/core/modules/ckeditor5/tests/src/Kernel/CKEditor5PluginManagerTest.php
    @@ -1519,4 +1546,363 @@ public function testExternalLinkAutomaticLinkDecoratorDisallowed(): void {
    +    foreach (['A', 'B'] as $derivative) {
    ...
    +      $definition['id'] = $derivative;
    ...
    +      $this->derivatives[$definition['id']] = new CKEditor5PluginDefinition($definition);
    

    I think s/derivative/id would be clearer, making both of the subsequent lines

    $definition['id'] = $id;
    ...
    $this->derivatives[$id] = new CKEditor5PluginDefinition($definition);
    

    This sort of resolves #39, no? Since the deriver doesn't have to do anything extra anymore

RTBC after the nits

Wim Leers’s picture

#41.1: INDEED! 😄

Excellent nits, addressed them 😊

Wim Leers’s picture

  1. Found one more doc line that needed updating like #41.2
  2. Thanks to the changes in #40, we can now construct CKEditor5PluginDefinition instances in the provider, which both simplifies the assertions and makes them easier to understand
  3. Figured out why the tests failed on 9.4 only: Drupal\Component\DependencyInjection\ContainerInterface was introduced by #2531564: Fix leaky and brittle container serialization solution. We don't need that in this particular test helper, we can rely on its parent, Symfony\Component\DependencyInjection\ContainerInterface.
Wim Leers’s picture

Looks like #43 will be green on all 4 branches 😊👍

Change record updated to match the current state of the patch 👍

Wim Leers’s picture

Actually, there's one more thing I think this should do: throw an exception just like \Drupal\Core\Config\TypedConfigManager::alterDefinitions() does when it detects an "alter hook" implementation adding new CKEditor 5 plugins — that should be forbidden.

Thoughts, @tim.plunkett?

tim.plunkett’s picture

Status: Needs review » Reviewed & tested by the community

#45 I think that changing hook_ckeditor5_plugin_info_alter like that should be in a follow-up.

Interdiffs in #42 and #43 look great, the whole patch is looking very clean.
RTBC!

Wim Leers’s picture

effulgentsia’s picture

Saving issue credits.

  • effulgentsia committed 4ff2694 on 10.1.x
    Issue #3313473 by Wim Leers, tim.plunkett, effulgentsia, smustgrave,...

  • effulgentsia committed 7e73ad2 on 10.0.x
    Issue #3313473 by Wim Leers, tim.plunkett, effulgentsia, smustgrave,...

  • effulgentsia committed b472d17 on 9.5.x
    Issue #3313473 by Wim Leers, tim.plunkett, effulgentsia, smustgrave,...
effulgentsia’s picture

Title: CKEditor 5 plugin definitions should be derivable » [Cherry pick to 9.4?] CKEditor 5 plugin definitions should be derivable
Version: 10.1.x-dev » 9.4.x-dev

#43 looks great. I pushed this to 10.1.x, cherry picked down to 9.5.x, and published the CR with 9.5.0-rc1 as the initial version.

Leaving this at RTBC for potential cherry picking to 9.4.x, but I'll check with a release manager first. Normally, I would consider this to be out of scope for a patch release, but CKE5 is experimental in Drupal 9.4, which makes this potentially in scope, and it would help contrib modules that want to use this functionality not need a separate branch or code path to handle 9.5/10.0 and 9.4.

alexpott’s picture

We've already backported many API changes to Ckeditor5 to Drupal 9.4.x.

catch’s picture

Yes I think backport is fine here.

alexpott’s picture

Title: [Cherry pick to 9.4?] CKEditor 5 plugin definitions should be derivable » CKEditor 5 plugin definitions should be derivable
Status: Reviewed & tested by the community » Fixed

Did the cherry-pick as it preserves the commit authorship :)

alexpott’s picture

Wim Leers’s picture

Wonderful, thank you so much! 😊 This means #3272732: Drupal 10 & CKEditor 5 readiness is unblocked! 👍

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.