getValue());
+ $source_text_area->setValue('temp
');
+ $source_text_area->setValue($new_value);
+ $this->pressEditorButton('Source');
+
// When unrestricted, additional attributes on links should be retained.
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
+ $this->assertCount($unrestricted ? 1 : 0, $xpath->query('//a[@href="http://www.drupal.org/association" and @class="trusted"]'));
+
+ // Save the entity whose text field is being edited.
+ $page->pressButton('Save');
+
+ // Assert the HTML the end user sees.
+ $assert_session->elementExists('css', $unrestricted
+ ? 'a[href="http://www.drupal.org/association"].trusted img[src*="image-test.png"]'
+ : 'a[href="http://www.drupal.org/association"] img[src*="image-test.png"]');
+
+ // Go back to edit the now *linked* . Everything from this
+ // point onwards is effectively testing "upcasting" and proving there is no
+ // data loss.
+ $this->drupalGet($this->host->toUrl('edit-form'));
+ $this->waitForEditor();
+
+ // Assert the "dataDowncast" HTML before making changes.
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
+ $this->assertNotEmpty($xpath->query('//img[@alt="drupalimage test image"]'));
+ $this->assertNotEmpty($xpath->query('//a[@href="http://www.drupal.org/association"]'));
+ $this->assertNotEmpty($xpath->query('//a[@href="http://www.drupal.org/association"]/img[@alt="drupalimage test image"]'));
$this->assertCount($unrestricted ? 1 : 0, $xpath->query('//a[@href="http://www.drupal.org/association" and @class="trusted"]'));
// Tests unlinking images.
@@ -224,8 +289,7 @@ public function testLinkability(string $image_type, bool $unrestricted) {
$assert_session->elementExists('css', '.ck-content .ck-widget.' . $expected_widget_class . ' > img[src*="image-test.png"][alt="drupalimage test image"]');
// Assert the "dataDowncast" HTML after making changes.
- $dom = Html::load($this->getSource());
- $xpath = new \DOMXPath($dom);
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
$this->assertCount(0, $xpath->query('//a[@href="http://www.drupal.org/association"]/img[@alt="drupalimage test image"]'));
$this->assertCount(1, $xpath->query('//img[@alt="drupalimage test image"]'));
// @todo Remove the different assertion for the "inline, unrestricted" case when https://www.drupal.org/project/ckeditor5/issues/3247634 is fixed.
@@ -237,7 +301,7 @@ public function testLinkability(string $image_type, bool $unrestricted) {
}
}
- public function imageTypes(): array {
+ public function providerLinkability(): array {
return [
'BLOCK image, restricted' => ['block', FALSE],
'BLOCK image, unrestricted' => ['block', TRUE],
@@ -246,21 +310,4 @@ public function imageTypes(): array {
];
}
- /**
- * Switches to source view, gets the value, switches back, and returns it.
- *
- * @return string
- * The current CKEditor 5 source value.
- */
- protected function getSource(): string {
- $assert_session = $this->assertSession();
- $this->pressEditorButton('Source');
- $this->assertNotEmpty($assert_session->waitForElement('css', '.ck-source-editing-area'));
- $source_value = $assert_session
- ->elementExists('css', '.ck-source-editing-area textarea')
- ->getValue();
- $this->pressEditorButton('Source');
- return $source_value;
- }
-
}
diff --git a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php
index bb9036deb9..0d3f6fe754 100644
--- a/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php
+++ b/core/modules/ckeditor5/tests/src/FunctionalJavascript/MediaTest.php
@@ -2,7 +2,6 @@
namespace Drupal\Tests\ckeditor5\FunctionalJavascript;
-use Drupal\Component\Utility\Html;
use Drupal\editor\Entity\Editor;
use Drupal\file\Entity\File;
use Drupal\filter\Entity\FilterFormat;
@@ -72,6 +71,12 @@ protected function setUp(): void {
'format' => 'test_format',
'name' => 'Test format',
'filters' => [
+ 'filter_html' => [
+ 'status' => TRUE,
+ 'settings' => [
+ 'allowed_html' => ' ',
+ ],
+ ],
'filter_align' => ['status' => TRUE],
'filter_caption' => ['status' => TRUE],
'media_embed' => ['status' => TRUE],
@@ -85,8 +90,6 @@ protected function setUp(): void {
'items' => [
'sourceEditing',
'link',
- 'italic',
- 'bold',
],
],
'plugins' => [
@@ -315,9 +318,7 @@ public function testAlt() {
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media img'));
// Test that by default no alt attribute is present on the drupal-media
// element.
- $this->pressEditorButton('Source');
$this->assertSourceAttributeSame('alt', NULL);
- $this->pressEditorButton('Source');
// Test that the preview shows the alt value from the media field's
// alt text.
$this->assertNotEmpty($assert_session->waitForElementVisible('css', '.ck-widget.drupal-media img[alt*="default alt"]'));
@@ -349,11 +350,9 @@ public function testAlt() {
// Test that the downcast drupal-media element now has the alt attribute
// entered in the dialog.
- $this->pressEditorButton('Source');
$this->assertSourceAttributeSame('alt', $who_is_zartan);
// The alt field should now display the override instead of the default.
- $this->pressEditorButton('Source');
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
@@ -373,12 +372,10 @@ public function testAlt() {
// Test that the downcast drupal-media element now has the alt attribute
// entered in the dialog.
- $this->pressEditorButton('Source');
$this->assertSourceAttributeSame('alt', $cobra_commander_bio);
// The default value of the alt field should now display the override
// instead of the value on the media image field.
- $this->pressEditorButton('Source');
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
@@ -399,13 +396,11 @@ public function testAlt() {
// Test that the downcast drupal-media element's alt attribute now has the
// empty string indicator.
- $this->pressEditorButton('Source');
$this->assertSourceAttributeSame('alt', '""');
// Test that setting alt to back to an empty string within the dialog will
// restore the default alt value saved in to the media image field of the
// media item.
- $this->pressEditorButton('Source');
$this->getBalloonButton('Override media image text alternative')->click();
$this->assertVisibleBalloon('.ck-text-alternative-form');
$alt_override_input = $page->find('css', '.ck-balloon-panel .ck-text-alternative-form input[type=text]');
@@ -415,7 +410,6 @@ public function testAlt() {
// Test that the downcast drupal-media element no longer has an alt
// attribute.
- $this->pressEditorButton('Source');
$this->assertSourceAttributeSame('alt', NULL);
}
@@ -428,21 +422,148 @@ public function testTranslationAlt() {
}
/**
- * Tests linkability of the CKEditor widget.
+ * Tests linkability of the media CKEditor widget.
+ *
+ * Due to the very different HTML markup generated for the editing view and
+ * the data view, this is explicitly testing the "editingDowncast" and
+ * "dataDowncast" results. These are CKEditor 5 concepts.
+ *
+ * @see https://ckeditor.com/docs/ckeditor5/latest/framework/guides/architecture/editing-engine.html#conversion
+ *
+ * @dataProvider providerLinkability
*/
- public function testLinkability() {
+ public function testLinkability(bool $unrestricted) {
+ // Disable filter_html.
+ if ($unrestricted) {
+ FilterFormat::load('test_format')
+ ->setFilterConfig('filter_html', ['status' => FALSE])
+ ->save();
+ }
+
$page = $this->getSession()->getPage();
$this->drupalGet($this->host->toUrl('edit-form'));
$this->waitForEditor();
$assert_session = $this->assertSession();
- // Select the CKEditor Widget.
- $drupalmedia = $assert_session->waitForElementVisible('css', '.ck-widget.drupal-media img');
+ // Initial state: the Drupal Media CKEditor Widget is not selected.
+ $drupalmedia = $assert_session->waitForElementVisible('css', '.ck-content .ck-widget.drupal-media');
$this->assertNotEmpty($drupalmedia);
+ $this->assertFalse($drupalmedia->hasClass('.ck-widget_selected'));
+
+ // Assert the "editingDowncast" HTML before making changes.
+ $assert_session->elementExists('css', '.ck-content .ck-widget.drupal-media > [data-drupal-media-preview]');
+
+ // Assert the "dataDowncast" HTML before making changes.
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
+ $this->assertNotEmpty($xpath->query('//drupal-media'));
+ $this->assertEmpty($xpath->query('//a'));
+
+ // Assert the link button is present and not pressed.
+ $link_button = $this->getEditorButton('Link');
+ $this->assertSame('false', $link_button->getAttribute('aria-pressed'));
+
+ // Wait for the preview to load.
+ $preview = $assert_session->waitForElement('css', '.ck-content .ck-widget.drupal-media [data-drupal-media-preview="ready"]');
+ $this->assertNotEmpty($preview);
+
+ // Tests linking Drupal media.
$drupalmedia->click();
+ $this->assertTrue($drupalmedia->hasClass('ck-widget_selected'));
+ $this->assertEditorButtonEnabled('Link');
+ // Assert structure of image toolbar balloon.
+ $this->assertVisibleBalloon('.ck-toolbar[aria-label="Drupal Media toolbar"]');
+ $link_media_button = $this->getBalloonButton('Link media');
+ // Click the "Link media" button.
+ $this->assertSame('false', $link_media_button->getAttribute('aria-pressed'));
+ $link_media_button->press();
+ // Assert structure of link form balloon.
+ $balloon = $this->assertVisibleBalloon('.ck-link-form');
+ $url_input = $balloon->find('css', '.ck-labeled-field-view__input-wrapper .ck-input-text');
+ // Fill in link form balloon's and hit "Save".
+ $url_input->setValue('http://linking-embedded-media.com');
+ $balloon->pressButton('Save');
+
+ // Assert the "editingDowncast" HTML after making changes. Assert the link
+ // exists, then assert the link exists. Then assert the expected DOM
+ // structure in detail.
+ $assert_session->elementExists('css', '.ck-content a[href="http://linking-embedded-media.com"]');
+ $assert_session->elementExists('css', '.ck-content .drupal-media.ck-widget > a[href="http://linking-embedded-media.com"] > div[aria-label] > article > div > img[src*="image-test.png"]');
+
+ // Assert the "dataDowncast" HTML after making changes.
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
+ $this->assertNotEmpty($xpath->query('//drupal-media'));
+ $this->assertNotEmpty($xpath->query('//a[@href="http://linking-embedded-media.com"]'));
+ $this->assertNotEmpty($xpath->query('//a[@href="http://linking-embedded-media.com"]/drupal-media'));
+
+ // Add `class="trusted"` to the link.
+ $this->assertEmpty($xpath->query('//a[@href="http://linking-embedded-media.com" and @class="trusted"]'));
+ $this->pressEditorButton('Source');
+ $source_text_area = $assert_session->waitForElement('css', '.ck-source-editing-area textarea');
+ $this->assertNotEmpty($source_text_area);
+ $new_value = str_replace('getValue());
+ $source_text_area->setValue('temp
');
+ $source_text_area->setValue($new_value);
+ $this->pressEditorButton('Source');
+
+ // When unrestricted, additional attributes on links should be retained.
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
+ $this->assertCount($unrestricted ? 1 : 0, $xpath->query('//a[@href="http://linking-embedded-media.com" and @class="trusted"]'));
+
+ // Save the entity whose text field is being edited.
+ $page->pressButton('Save');
- // @todo Make media widgets linkable in https://www.drupal.org/project/ckeditor5/issues/3246169 and port its test coverage.
+ // Assert the HTML the end user sees.
+
+ $assert_session->elementExists('css', $unrestricted
+ ? 'a[href="http://linking-embedded-media.com"].trusted img[src*="image-test.png"]'
+ : 'a[href="http://linking-embedded-media.com"] img[src*="image-test.png"]');
+
+ // Go back to edit the now *linked* . Everything from this
+ // point onwards is effectively testing "upcasting" and proving there is no
+ // data loss.
+ $this->drupalGet($this->host->toUrl('edit-form'));
+ $this->waitForEditor();
+
+ // Assert the "dataDowncast" HTML before making changes.
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
+ $this->assertNotEmpty($xpath->query('//drupal-media'));
+ $this->assertNotEmpty($xpath->query('//a[@href="http://linking-embedded-media.com"]'));
+ $this->assertNotEmpty($xpath->query('//a[@href="http://linking-embedded-media.com"]/drupal-media'));
+
+ // Tests unlinking media.
+ $drupalmedia->click();
+ $this->assertEditorButtonEnabled('Link');
+ $this->assertSame('true', $this->getEditorButton('Link')->getAttribute('aria-pressed'));
+ // Assert structure of Drupal media toolbar balloon.
+ $this->assertVisibleBalloon('.ck-toolbar[aria-label="Drupal Media toolbar"]');
+ $link_media_button = $this->getBalloonButton('Link media');
+ $this->assertSame('true', $link_media_button->getAttribute('aria-pressed'));
+ $link_media_button->click();
+ // Assert structure of link actions balloon.
+ $this->getBalloonButton('Edit link');
+ $unlink_image_button = $this->getBalloonButton('Unlink');
+ // Click the "Unlink" button.
+ $unlink_image_button->click();
+ $this->assertSame('false', $this->getEditorButton('Link')->getAttribute('aria-pressed'));
+
+ // Assert the "editingDowncast" HTML after making changes. Assert the link
+ // exists, then assert no link exists. Then assert the expected DOM
+ // structure in detail.
+ $assert_session->elementNotExists('css', '.ck-content a');
+ $assert_session->elementExists('css', '.ck-content .drupal-media.ck-widget > div[aria-label] > article > div > img[src*="image-test.png"]');
+
+ // Assert the "dataDowncast" HTML after making changes.
+ $xpath = new \DOMXPath($this->getEditorDataAsDom());
+ $this->assertNotEmpty($xpath->query('//drupal-media'));
+ $this->assertEmpty($xpath->query('//a'));
+ }
+
+ public function providerLinkability(): array {
+ return [
+ 'restricted' => [FALSE],
+ 'unrestricted' => [TRUE],
+ ];
}
/**
@@ -548,12 +669,14 @@ public function testViewMode() {
* doesn't have the attribute.
*/
protected function assertSourceAttributeSame($attribute, $value) {
- $this->assertNotEmpty($drupal_media = $this->getDrupalMediaFromSource());
+ $dom = $this->getEditorDataAsDom();
+ $drupal_media = (new \DOMXPath($dom))->query('//drupal-media');
+ $this->assertNotEmpty($drupal_media);
if ($value === NULL) {
- $this->assertFalse($drupal_media->hasAttribute($attribute));
+ $this->assertFalse($drupal_media[0]->hasAttribute($attribute));
}
else {
- $this->assertSame($value, $drupal_media->getAttribute($attribute));
+ $this->assertSame($value, $drupal_media[0]->getAttribute($attribute));
}
}
@@ -578,22 +701,4 @@ protected function getLastPreviewRequestTransferSize() {
return $this->getSession()->evaluateScript($javascript);
}
- /**
- * Parses the element from CKEditor's "source" view.
- *
- * Assumes CKEditor is in source mode.
- *
- * @return \DOMNode|null
- * The drupal-media element or NULL if it can't be found.
- */
- protected function getDrupalMediaFromSource() {
- $value = $this->assertSession()
- ->elementExists('css', '.ck-source-editing-area textarea')
- ->getValue();
- $dom = Html::load($value);
- $xpath = new \DOMXPath($dom);
- $list = $xpath->query('//drupal-media');
- return count($list) > 0 ? $list[0] : NULL;
- }
-
}
diff --git a/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php b/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php
index c1a2cc10fa..738e0574d6 100644
--- a/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php
+++ b/core/modules/ckeditor5/tests/src/Traits/CKEditor5TestTrait.php
@@ -3,6 +3,7 @@
namespace Drupal\Tests\ckeditor5\Traits;
use Behat\Mink\Element\NodeElement;
+use Drupal\Component\Utility\Html;
/**
* Provides methods to test CKEditor 5.
@@ -11,6 +12,26 @@
*/
trait CKEditor5TestTrait {
+ /**
+ * Gets CKEditor 5 instance data as a PHP DOMDocument.
+ *
+ * @return \DOMDocument
+ * The result of parsing CKEditor 5's data into a PHP DOMDocument.
+ *
+ * @see https://ckeditor.com/docs/ckeditor5/latest/api/module_editor-classic_classiceditor-ClassicEditor.html#function-getData
+ */
+ protected function getEditorDataAsDom(): \DOMDocument {
+ // We cannot trust on CKEditor updating the textarea every time model
+ // changes. Therefore, the most reliable way to get downcasted data is to
+ // use the CKEditor API.
+ $javascript = <<getSession()->evaluateScript($javascript));
+ }
+
/**
* Waits for CKEditor to initialize.
*/
@@ -52,6 +73,7 @@ protected function getEditorButton($name) {
*/
protected function assertEditorButtonDisabled($name) {
$button = $this->getEditorButton($name);
+ $this->assertTrue($button->hasAttribute('aria-disabled'));
$this->assertTrue($button->hasClass('ck-disabled'));
}
@@ -63,6 +85,7 @@ protected function assertEditorButtonDisabled($name) {
*/
protected function assertEditorButtonEnabled($name) {
$button = $this->getEditorButton($name);
+ $this->assertFalse($button->hasAttribute('aria-disabled'));
$this->assertFalse($button->hasClass('ck-disabled'));
}
diff --git a/core/package.json b/core/package.json
index bcce9ec738..0dbf864c57 100644
--- a/core/package.json
+++ b/core/package.json
@@ -37,7 +37,6 @@
"@babel/core": "^7.0.0",
"@babel/preset-env": "^7.0.0",
"@babel/register": "^7.7.7",
- "@ckeditor/ckeditor5-dev-utils": "^25.4.5",
"@ckeditor/ckeditor5-alignment": "~31.0.0",
"@ckeditor/ckeditor5-basic-styles": "~31.0.0",
"@ckeditor/ckeditor5-block-quote": "~31.0.0",
diff --git a/core/yarn.lock b/core/yarn.lock
index a0d11bd7e9..4224ee1ac2 100644
--- a/core/yarn.lock
+++ b/core/yarn.lock
@@ -2401,9 +2401,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001274:
- version "1.0.30001278"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001278.tgz#51cafc858df77d966b17f59b5839250b24417fff"
- integrity sha512-mpF9KeH8u5cMoEmIic/cr7PNS+F5LWBk0t2ekGT60lFf0Wq+n9LspAj0g3P+o7DQhD3sUdlMln4YFAWhFYn9jg==
+ version "1.0.30001279"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce"
+ integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==
caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001271:
version "1.0.30001271"
@@ -3361,9 +3361,9 @@ electron-to-chromium@^1.3.878:
integrity sha512-iwIP/6WoeSimzUKJIQtjtpVDsK8Ir8qQCMXsUBwg+rxJR2Uh3wTNSbxoYRfs+3UWx/9MAnPIxVZCyWkm8MT0uw==
electron-to-chromium@^1.3.886:
- version "1.3.890"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.890.tgz#e7143b659f73dc4d0512d1ae4baeb0fb9e7bc835"
- integrity sha512-VWlVXSkv0cA/OOehrEyqjUTHwV8YXCPTfPvbtoeU2aHR21vI4Ejh5aC4AxUwOmbLbBgb6Gd3URZahoCxtBqCYQ==
+ version "1.3.892"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.892.tgz#0e3f5bb1de577e2e5a6dffd5a4b278c4a735cd39"
+ integrity sha512-YDW4yIjdfMnbRoBjRZ/aNQYmT6JgQFLwmTSDRJMQdrY4MByEzppdXp3rnJ0g4LBWcsYTUvwKKClYN1ofZ0COOQ==
emoji-regex@^7.0.1:
version "7.0.3"
@@ -5703,13 +5703,25 @@ mime-db@1.50.0:
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.50.0.tgz#abd4ac94e98d3c0e185016c67ab45d5fde40c11f"
integrity sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==
-mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.19:
+mime-db@1.51.0:
+ version "1.51.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.51.0.tgz#d9ff62451859b18342d960850dc3cfb77e63fb0c"
+ integrity sha512-5y8A56jg7XVQx2mbv1lu49NR4dokRnhZYTtL+KGfaa27uq4pSTXkwQkFJl4pkRMyNFz/EtYDSkiiEHx3F7UN6g==
+
+mime-types@^2.1.12, mime-types@~2.1.19:
version "2.1.33"
resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.33.tgz#1fa12a904472fafd068e48d9e8401f74d3f70edb"
integrity sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==
dependencies:
mime-db "1.50.0"
+mime-types@^2.1.27:
+ version "2.1.34"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.34.tgz#5a712f9ec1503511a945803640fafe09d3793c24"
+ integrity sha512-6cP692WwGIs9XXdOO4++N+7qjqv0rqxxVvJ3VHPh/Sc9mVZcQP+ZGhkKiTvWMQRr2tbHkJP/Yn7Y0npb3ZBs4A==
+ dependencies:
+ mime-db "1.51.0"
+
mime@^2.3.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe"
@@ -8843,9 +8855,9 @@ webpack-sources@^3.2.0:
integrity sha512-t6BMVLQ0AkjBOoRTZgqrWm7xbXMBzD+XDq2EZ96+vMfn3qKgsvdXZhbPZ4ElUOpdv4u+iiGe+w3+J75iy/bYGA==
webpack@^5.51.1:
- version "5.62.1"
- resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.62.1.tgz#06f09b56a7b1bb13ed5137ad4b118358a90c9505"
- integrity sha512-jNLtnWChS2CMZ7vqWtztv0G6fYB5hz11Zsadp5tE7e4/66zVDj7/KUeQZOsOl8Hz5KrLJH1h2eIDl6AnlyE12Q==
+ version "5.62.2"
+ resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.62.2.tgz#9a352423396a3d434f8c3aef19dcfd79b58fe88a"
+ integrity sha512-GDJymz2MEpfcLoLHQFtV72raCVsuQtlUHeeNixbYh5DkFombEhoLyto3GU8xA42VfRXR7pxrYQ75Sd+YelFe5A==
dependencies:
"@types/eslint-scope" "^3.7.0"
"@types/estree" "^0.0.50"