diff --git a/docs/release-notes.md b/docs/release-notes.md index 08cf344..36086b9 100644 --- a/docs/release-notes.md +++ b/docs/release-notes.md @@ -1,5 +1,12 @@ ## Release Notes +### BootstrapComponents 6.1.1 + +Released on TBD + +Fixes: +* stop logging `Unexpected general module ... in styles queue.` on pages using a modal, carousel, tooltip or popover + ### BootstrapComponents 6.1.0 Released on 21-July-2026 diff --git a/src/HooksHandler.php b/src/HooksHandler.php index c942c4d..dadb1dc 100644 --- a/src/HooksHandler.php +++ b/src/HooksHandler.php @@ -16,6 +16,8 @@ use MediaWiki\Hook\SetupAfterCacheHook; use MediaWiki\MediaWikiServices; use MediaWiki\Parser\Parser; +use MediaWiki\Parser\ParserOutput; +use MediaWiki\ResourceLoader\Module; use SMW\Utils\File; use StripState; @@ -245,14 +247,29 @@ public function onParserAfterParse( $parser, &$text, $stripState ): bool { if ( !$this->getComponentLibrary()->isRegistered( $activeComponent ) ) { continue; } - foreach ( $this->getComponentLibrary()->getModulesFor( $activeComponent, 'vector' ) as $module ) { - $parser->getOutput()->addModuleStyles( [ $module ] ); - $parser->getOutput()->addModules( [ $module ] ); + foreach ( $this->getComponentLibrary()->getModulesFor( $activeComponent, 'vector' ) as $moduleName ) { + $this->addModuleToMatchingQueue( $parser->getOutput(), $moduleName ); } } return true; } + /** + * ResourceLoader discards anything but a styles-only module from the styles queue, so a module + * that also carries scripts, dependencies, messages or templates has to go to the general + * queue, which delivers its styles too. + */ + private function addModuleToMatchingQueue( ParserOutput $parserOutput, string $moduleName ): void { + $module = MediaWikiServices::getInstance()->getResourceLoader()->getModule( $moduleName ); + + if ( $module !== null && $module->getType() === Module::LOAD_STYLES ) { + $parserOutput->addModuleStyles( [ $moduleName ] ); + return; + } + + $parserOutput->addModules( [ $moduleName ] ); + } + /** * Hook: ParserFirstCallInit * diff --git a/tests/phpunit/Unit/HooksHandlerTest.php b/tests/phpunit/Unit/HooksHandlerTest.php index aa1fb6c..eb60356 100644 --- a/tests/phpunit/Unit/HooksHandlerTest.php +++ b/tests/phpunit/Unit/HooksHandlerTest.php @@ -83,6 +83,38 @@ public function testOnParserAfterParseAddsOnlyTheStylesFix() { $this->assertNotContains( 'ext.bootstrap.scripts', $parserOutput->getModules() ); } + public function testOnParserAfterParseSplitsComponentModulesByType() { + $parserOutput = $this->parserOutputWithActiveComponent( 'modal' ); + + $this->assertSame( + [ + 'ext.bootstrapComponents.bootstrap.fix', + 'ext.bootstrapComponents.button.fix', + 'ext.bootstrapComponents.modal.vector-fix', + ], + $parserOutput->getModuleStyles() + ); + $this->assertSame( [ 'ext.bootstrapComponents.modal.fix' ], $parserOutput->getModules() ); + } + + private function parserOutputWithActiveComponent( string $componentName ): ParserOutput { + $bootstrapComponentsService = new BootstrapComponentsService( new TestConfig() ); + $bootstrapComponentsService->registerComponentAsActive( $componentName ); + $parserOutput = new ParserOutput(); + $parser = $this->createMock( Parser::class ); + $parser->method( 'getOutput' )->willReturn( $parserOutput ); + $text = ''; + + $hooksHandler = new HooksHandler( + $bootstrapComponentsService, + new ComponentLibrary(), + $this->createMock( NestingController::class ), + ); + $hooksHandler->onParserAfterParse( $parser, $text, null ); + + return $parserOutput; + } + public function testOnBeforePageDisplayLoadsExtensionBootstrapOnNonProviderContentPage() { $out = $this->createMock( OutputPage::class ); $out->method( 'getModuleStyles' )->willReturn( [ 'ext.bootstrapComponents.bootstrap.fix' ] );