Merge "Hygiene: use null-coalescing operator when operating on tags"
[lhc/web/wiklou.git] / includes / resourceloader / ResourceLoaderModule.php
index a507ad3..eb174f0 100644 (file)
@@ -159,8 +159,20 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
         * Get all JS for this module for a given language and skin.
         * Includes all relevant JS except loader scripts.
         *
+        * For "plain" script modules, this should return a string with JS code. For multi-file modules
+        * where require() is used to load one file from another file, this should return an array
+        * structured as follows:
+        * [
+        *     'files' => [
+        *         'file1.js' => [ 'type' => 'script', 'content' => 'JS code' ],
+        *         'file2.js' => [ 'type' => 'script', 'content' => 'JS code' ],
+        *         'data.json' => [ 'type' => 'data', 'content' => array ]
+        *     ],
+        *     'main' => 'file1.js'
+        * ]
+        *
         * @param ResourceLoaderContext $context
-        * @return string JavaScript code
+        * @return string|array JavaScript code (string), or multi-file structure described above (array)
         */
        public function getScript( ResourceLoaderContext $context ) {
                // Stub, override expected
@@ -691,7 +703,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
 
                // This MUST build both scripts and styles, regardless of whether $context->getOnly()
                // is 'scripts' or 'styles' because the result is used by getVersionHash which
-               // must be consistent regardles of the 'only' filter on the current request.
+               // must be consistent regardless of the 'only' filter on the current request.
                // Also, when introducing new module content resources (e.g. templates, headers),
                // these should only be included in the array when they are non-empty so that
                // existing modules not using them do not get their cache invalidated.
@@ -722,7 +734,6 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                }
                $content['scripts'] = $scripts;
 
-               // Styles
                $styles = [];
                // Don't create empty stylesheets like [ '' => '' ] for modules
                // that don't *have* any stylesheets (T40024).
@@ -813,7 +824,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                        } else {
                                // Infer changes based on definition and other metrics
                                $summary = $this->getDefinitionSummary( $context );
-                               if ( !isset( $summary['_cacheEpoch'] ) ) {
+                               if ( !isset( $summary['_class'] ) ) {
                                        throw new LogicException( 'getDefinitionSummary must call parent method' );
                                }
                                $str = json_encode( $summary );
@@ -883,7 +894,9 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
        public function getDefinitionSummary( ResourceLoaderContext $context ) {
                return [
                        '_class' => static::class,
-                       '_cacheEpoch' => $this->getConfig()->get( 'CacheEpoch' ),
+                       // Make sure that when filter cache for minification is invalidated,
+                       // we also change the HTTP urls and mw.loader.store keys (T176884).
+                       '_cacheVersion' => ResourceLoader::CACHE_VERSION,
                ];
        }
 
@@ -930,7 +943,7 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                if ( !$this->getConfig()->get( 'ResourceLoaderValidateJS' ) ) {
                        return $contents;
                }
-               $cache = ObjectCache::getMainWANInstance();
+               $cache = MediaWikiServices::getInstance()->getMainWANObjectCache();
                return $cache->getWithSetCallback(
                        $cache->makeGlobalKey(
                                'resourceloader',
@@ -942,16 +955,25 @@ abstract class ResourceLoaderModule implements LoggerAwareInterface {
                        $cache::TTL_WEEK,
                        function () use ( $contents, $fileName ) {
                                $parser = self::javaScriptParser();
+                               $err = null;
                                try {
+                                       Wikimedia\suppressWarnings();
                                        $parser->parse( $contents, $fileName, 1 );
-                                       $result = $contents;
                                } catch ( Exception $e ) {
-                                       // We'll save this to cache to avoid having to re-validate broken JS
-                                       $err = $e->getMessage();
-                                       $result = "mw.log.error(" .
-                                               Xml::encodeJsVar( "JavaScript parse error: $err" ) . ");";
+                                       $err = $e;
+                               } finally {
+                                       Wikimedia\restoreWarnings();
+                               }
+                               if ( $err ) {
+                                       // Send the error to the browser console client-side.
+                                       // By returning this as replacement for the actual script,
+                                       // we ensure modules are safe to load in a batch request,
+                                       // without causing other unrelated modules to break.
+                                       return 'mw.log.error(' .
+                                               Xml::encodeJsVar( 'JavaScript parse error: ' . $err->getMessage() ) .
+                                               ');';
                                }
-                               return $result;
+                               return $contents;
                        }
                );
        }