diff -u b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php --- b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php +++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryCollector.php @@ -137,7 +137,9 @@ if (!empty($libraries_extend["$extension/$library_name"])) { foreach ($libraries_extend["$extension/$library_name"] as $library_extend_name) { if (isset($library_definition['deprecated'])) { - @trigger_error(sprintf('Deprecated library extend %s detected in %s.', "$extension/$library_name", $this->themeManager->getActiveTheme()->getName()), E_USER_DEPRECATED); + $extend_message = sprintf('Theme "%s" is extending a deprecated library.', $extension); + $library_deprecation = str_replace('%library_id%', "$extension/$library_name", $library_definition['deprecated']); + @trigger_error(sprintf('%s %s', $extend_message, $library_deprecation), E_USER_DEPRECATED); } if (!is_string($library_extend_name)) { // Only string library names are allowed. diff -u b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php --- b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php +++ b/core/lib/Drupal/Core/Asset/LibraryDiscoveryParser.php @@ -383,7 +383,9 @@ // Process libraries overrides. if (isset($libraries_overrides["$extension/$library_name"])) { if (isset($library['deprecated'])) { - @trigger_error(sprintf('Deprecated library override %s detected in %s.', "$extension/$library_name", $active_theme->getName()), E_USER_DEPRECATED); + $override_message = sprintf('Theme "%s" is overriding a deprecated library.', $extension); + $library_deprecation = str_replace('%library_id%', "$extension/$library_name", $library['deprecated']); + @trigger_error(sprintf('%s %s', $override_message, $library_deprecation), E_USER_DEPRECATED); } // Active theme defines an override for this library. $override_definition = $libraries_overrides["$extension/$library_name"]; only in patch2: unchanged: --- a/core/modules/system/tests/modules/theme_test/theme_test.libraries.yml +++ b/core/modules/system/tests/modules/theme_test/theme_test.libraries.yml @@ -5,3 +5,17 @@ theme_stylesheets_override_and_remove_test: css/base-remove.css: {} css/sub-override.css: {} css/sub-remove.css: {} + +deprecated_library: + version: VERSION + css: + base: + css/foo.css: {} + deprecated: 'The "%library_id%" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use another library instead. See https://www.example.com' + +another_deprecated_library: + version: VERSION + css: + base: + css/bar.css: {} + deprecated: 'The "%library_id%" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use another library instead. See https://www.example.com' only in patch2: unchanged: --- a/core/modules/system/tests/themes/test_legacy_theme/test_legacy_theme.info.yml +++ b/core/modules/system/tests/themes/test_legacy_theme/test_legacy_theme.info.yml @@ -4,3 +4,13 @@ description: 'Test theme to test deprecated functionality.' version: VERSION package: Testing base theme: test_theme + +libraries-override: + theme_test/deprecated_library: + css: + base: + css/foo.css: css/bar.css + +libraries-extend: + theme_test/another_deprecated_library: + - test_legacy_theme/library only in patch2: unchanged: --- /dev/null +++ b/core/modules/system/tests/themes/test_legacy_theme/test_legacy_theme.libraries.yml @@ -0,0 +1,4 @@ +library: + css: + base: + css/baz.css: {} only in patch2: unchanged: --- a/core/tests/Drupal/KernelTests/Core/Asset/LibraryDiscoveryIntegrationTest.php +++ b/core/tests/Drupal/KernelTests/Core/Asset/LibraryDiscoveryIntegrationTest.php @@ -20,6 +20,11 @@ class LibraryDiscoveryIntegrationTest extends KernelTestBase { */ protected $libraryDiscovery; + /** + * {@inheritdoc} + */ + protected static $modules = ['theme_test']; + /** * {@inheritdoc} */ @@ -208,6 +213,33 @@ public function testLibrariesExtend() { } } + /** + * Test deprecated libraries. + */ + public function testDeprecatedLibrary() { + $deprecations = []; + set_error_handler(function ($severity, $message) use (&$deprecations) { + // Convert deprecation error into a catchable exception. + if ($severity === E_USER_DEPRECATED) { + $deprecations[] = $message; + return function() { return true; }; + } + }); + + $this->activateTheme('test_legacy_theme'); + $this->libraryDiscovery->getLibraryByName('theme_test', 'deprecated_library'); + $this->libraryDiscovery->getLibraryByName('theme_test', 'another_deprecated_library'); + $expected_derecations = [ + 'Theme "theme_test" is overriding a deprecated library. The "theme_test/deprecated_library" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use another library instead. See https://www.example.com', + 'Theme "theme_test" is extending a deprecated library. The "theme_test/another_deprecated_library" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use another library instead. See https://www.example.com', + 'The "theme_test/deprecated_library" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use another library instead. See https://www.example.com', + 'The "theme_test/another_deprecated_library" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use another library instead. See https://www.example.com', + ]; + $this->assertSame($expected_derecations, $deprecations); + + restore_error_handler(); + } + /** * Activates a specified theme. * only in patch2: unchanged: --- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryCollectorTest.php @@ -61,6 +61,23 @@ class LibraryDiscoveryCollectorTest extends UnitTestCase { 'js' => [], 'css' => [], ], + 'test_3' => [ + 'js' => [], + 'css' => [ + 'theme' => [ + 'foo.css' => [], + ], + ], + ], + 'test_4' => [ + 'js' => [], + 'css' => [ + 'theme' => [ + 'bar.css' => [], + ], + ], + 'deprecated' => 'The "%library_id%" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use the test_3 library instead. See https://www.example.com', + ], ]; protected $activeTheme; @@ -77,7 +94,6 @@ protected function setUp(): void { $this->libraryDiscoveryParser = $this->getMockBuilder('Drupal\Core\Asset\LibraryDiscoveryParser') ->disableOriginalConstructor() ->getMock(); - } /** @@ -89,7 +105,7 @@ public function testResolveCacheMiss() { $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') ->disableOriginalConstructor() ->getMock(); - $this->themeManager->expects($this->exactly(3)) + $this->themeManager->expects($this->exactly(5)) ->method('getActiveTheme') ->willReturn($this->activeTheme); $this->activeTheme->expects($this->once()) @@ -115,7 +131,7 @@ public function testDestruct() { $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') ->disableOriginalConstructor() ->getMock(); - $this->themeManager->expects($this->exactly(3)) + $this->themeManager->expects($this->exactly(5)) ->method('getActiveTheme') ->willReturn($this->activeTheme); $this->activeTheme->expects($this->once()) @@ -150,4 +166,94 @@ public function testDestruct() { $this->libraryDiscoveryCollector->destruct(); } + /** + * Tests library with an extend. + * + * @covers ::applyLibrariesExtend + */ + public function testLibrariesExtend() { + $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') + ->disableOriginalConstructor() + ->getMock(); + $this->themeManager->expects($this->any()) + ->method('getActiveTheme') + ->willReturn($this->activeTheme); + $this->activeTheme->expects($this->once()) + ->method('getName') + ->willReturn('kitten_theme'); + $this->activeTheme->expects($this->atLeastOnce()) + ->method('getLibrariesExtend') + ->willReturn([ + 'test/test_3' => [ + 'kitten_theme/extend' + ], + ]); + $this->libraryDiscoveryParser->expects($this->at(0)) + ->method('buildByExtension') + ->with('test') + ->willReturn($this->libraryData); + $this->libraryDiscoveryParser->expects($this->at(1)) + ->method('buildByExtension') + ->with('kitten_theme') + ->willReturn([ + 'extend' => [ + 'css' => [ + 'theme' => [ + 'baz.css' => [] + ], + ], + ], + ]); + $library_discovery_collector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager); + $libraries = $library_discovery_collector->get('test'); + $this->assertSame(['foo.css', 'baz.css'], array_keys($libraries['test_3']['css']['theme'])); + } + + /** + * Tests a deprecated library with an extend. + * + * @covers ::applyLibrariesExtend + */ + public function testLibrariesExtendDeprecated() { + $previous_error_handler = set_error_handler(function ($severity, $message, $file, $line, $context) use (&$previous_error_handler) { + // Convert deprecation error into a catchable exception. + if ($severity === E_USER_DEPRECATED) { + throw new \ErrorException($message, 0, $severity, $file, $line); + } + if ($previous_error_handler) { + return $previous_error_handler($severity, $message, $file, $line, $context); + } + }); + + $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') + ->disableOriginalConstructor() + ->getMock(); + $this->themeManager->expects($this->any()) + ->method('getActiveTheme') + ->willReturn($this->activeTheme); + $this->activeTheme->expects($this->once()) + ->method('getName') + ->willReturn('kitten_theme'); + $this->activeTheme->expects($this->atLeastOnce()) + ->method('getLibrariesExtend') + ->willReturn([ + 'test/test_4' => [ + 'kitten_theme/extend' + ], + ]); + $this->libraryDiscoveryParser->expects($this->at(0)) + ->method('buildByExtension') + ->with('test') + ->willReturn($this->libraryData); + $library_discovery_collector = new LibraryDiscoveryCollector($this->cache, $this->lock, $this->libraryDiscoveryParser, $this->themeManager); + try { + $library_discovery_collector->get('test'); + $this->fail('No deprecation error triggered.'); + } + catch (\ErrorException $e) { + $this->assertSame('Theme "test" is extending a deprecated library. The "test/test_4" asset library is deprecated in drupal:9.1.0 and is removed from drupal:9.0.0. Use the test_3 library instead. See https://www.example.com', $e->getMessage()); + } + + restore_error_handler(); + } } only in patch2: unchanged: --- a/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php +++ b/core/tests/Drupal/Tests/Core/Asset/LibraryDiscoveryParserTest.php @@ -49,6 +49,13 @@ class LibraryDiscoveryParserTest extends UnitTestCase { */ protected $themeManager; + /** + * The mocked active theme. + * + * @var \Drupal\Core\Theme\ActiveTheme|\PHPUnit\Framework\MockObject\MockObject + */ + protected $activeTheme; + /** * The mocked lock backend. * @@ -78,15 +85,15 @@ protected function setUp(): void { $this->moduleHandler = $this->createMock('Drupal\Core\Extension\ModuleHandlerInterface'); $this->themeManager = $this->createMock('Drupal\Core\Theme\ThemeManagerInterface'); - $mock_active_theme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') + $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') ->disableOriginalConstructor() ->getMock(); - $mock_active_theme->expects($this->any()) + $this->activeTheme->expects($this->any()) ->method('getLibrariesOverride') ->willReturn([]); $this->themeManager->expects($this->any()) ->method('getActiveTheme') - ->willReturn($mock_active_theme); + ->willReturn($this->activeTheme); $this->streamWrapperManager = $this->createMock(StreamWrapperManagerInterface::class); $this->librariesDirectoryFileFinder = $this->createMock(LibrariesDirectoryFileFinder::class); $this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler, $this->themeManager, $this->streamWrapperManager, $this->librariesDirectoryFileFinder); @@ -111,7 +118,7 @@ public function testBuildByExtensionSimple() { $library = $libraries['example']; $this->assertCount(0, $library['js']); - $this->assertCount(1, $library['css']); + $this->assertCount(2, $library['css']); $this->assertCount(0, $library['dependencies']); $this->assertEquals($path . '/css/example.css', $library['css'][0]['data']); @@ -544,6 +551,113 @@ public function testLibraryWithLicenses() { $this->assertEquals($library['license'], $expected_license); } + /** + * Tests libraries with overrides. + * + * @covers ::applyLibrariesOverride + */ + public function testLibraryOverride() { + $mock_theme_path = 'mocked_themes/kittens'; + $this->themeManager = $this->createMock('Drupal\Core\Theme\ThemeManagerInterface'); + $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') + ->disableOriginalConstructor() + ->getMock(); + $this->activeTheme->expects($this->atLeastOnce()) + ->method('getLibrariesOverride') + ->willReturn([ + $mock_theme_path => [ + 'example_module/example' => [ + 'css' => [ + 'theme' => [ + 'css/example.css' => 'css/overridden.css', + 'css/example2.css' => FALSE, + ], + ], + ], + ], + ]); + $this->themeManager->expects($this->any()) + ->method('getActiveTheme') + ->willReturn($this->activeTheme); + $this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler, $this->themeManager, $this->streamWrapperManager, $this->librariesDirectoryFileFinder); + + $this->moduleHandler->expects($this->atLeastOnce()) + ->method('moduleExists') + ->with('example_module') + ->will($this->returnValue(TRUE)); + + $path = __DIR__ . '/library_test_files'; + $path = substr($path, strlen($this->root) + 1); + $this->libraryDiscoveryParser->setPaths('module', 'example_module', $path); + + $libraries = $this->libraryDiscoveryParser->buildByExtension('example_module'); + $library = $libraries['example']; + + $this->assertCount(0, $library['js']); + $this->assertCount(1, $library['css']); + $this->assertCount(0, $library['dependencies']); + $this->assertEquals($mock_theme_path . '/css/overridden.css', $library['css'][0]['data']); + } + + /** + * Tests deprecated library with an override. + * + * @covers ::applyLibrariesOverride + */ + public function testLibraryOverrideDeprecated() { + $previous_error_handler = set_error_handler(function ($severity, $message, $file, $line, $context) use (&$previous_error_handler) { + // Convert deprecation error into a catchable exception. + if ($severity === E_USER_DEPRECATED) { + throw new \ErrorException($message, 0, $severity, $file, $line); + } + if ($previous_error_handler) { + return $previous_error_handler($severity, $message, $file, $line, $context); + } + }); + + $mock_theme_path = 'mocked_themes/kittens'; + $this->themeManager = $this->createMock('Drupal\Core\Theme\ThemeManagerInterface'); + $this->activeTheme = $this->getMockBuilder('Drupal\Core\Theme\ActiveTheme') + ->disableOriginalConstructor() + ->getMock(); + $this->activeTheme->expects($this->atLeastOnce()) + ->method('getLibrariesOverride') + ->willReturn([ + $mock_theme_path => [ + 'deprecated/deprecated' => [ + 'css' => [ + 'theme' => [ + 'css/example.css' => 'css/overridden.css', + ], + ], + ], + ], + ]); + $this->themeManager->expects($this->any()) + ->method('getActiveTheme') + ->willReturn($this->activeTheme); + $this->libraryDiscoveryParser = new TestLibraryDiscoveryParser($this->root, $this->moduleHandler, $this->themeManager, $this->streamWrapperManager, $this->librariesDirectoryFileFinder); + + $this->moduleHandler->expects($this->atLeastOnce()) + ->method('moduleExists') + ->with('deprecated') + ->will($this->returnValue(TRUE)); + + $path = __DIR__ . '/library_test_files'; + $path = substr($path, strlen($this->root) + 1); + $this->libraryDiscoveryParser->setPaths('module', 'deprecated', $path); + + try { + $this->libraryDiscoveryParser->buildByExtension('deprecated'); + $this->fail('No deprecation error triggered.'); + } + catch (\ErrorException $e) { + $this->assertSame('Theme "deprecated" is overriding a deprecated library. The "deprecated/deprecated" asset library is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use another library instead. See https://www.example.com', $e->getMessage()); + } + + restore_error_handler(); + } + /** * Verifies assertions catch invalid CSS declarations. * only in patch2: unchanged: --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Asset/library_test_files/deprecated.libraries.yml @@ -0,0 +1,6 @@ +deprecated: + version: VERSION + css: + theme: + css/example.css: {} + deprecated: 'The "%library_id%" asset library is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Use another library instead. See https://www.example.com' only in patch2: unchanged: --- a/core/tests/Drupal/Tests/Core/Asset/library_test_files/example_module.libraries.yml +++ b/core/tests/Drupal/Tests/Core/Asset/library_test_files/example_module.libraries.yml @@ -3,3 +3,4 @@ example: css: theme: css/example.css: {} + css/example2.css: {}