Front-end:
* New mw.loader method: addSource(). Call with two arguments or an object as first argument for multiple registrations
* New property in module registry: "source". Optional for local modules (falls back to 'local'). When loading/using one or more modules, the worker will group the request by source and make separate requests to the sources as needed.
* Re-arranging object properties in mw.loader.register to match the same order all other code parts use.
* Adding documentation for 'source' and where missing updating it to include 'group' as well.
* Refactor of mw.loader.work() by Roan Kattouw and Timo Tijhof:'
-- Additional splitting layer by source (in addition to splitting by group), renamed 'groups' to 'splits'
-- Clean up of the loop, and removing a no longer needed loop after the for-in-loop
-- Much more function documentation in mw.loader.work()
-- Moved caching of wgResourceLoaderMaxQueryLength out of the loop and renamed 'limit' to 'maxQueryLength
Back-end changed provided through patch by Roan Kattouw (to avoid broken code between commits):
* New method in ResourceLoader: addSource(). During construction of ResourceLoader this will be called by default for 'local' with loadScript property set to $wgLoadScript. Additional sources can be registered through $wgResourceLoaderSources (empty array by default)
* Calling mw.loader.addSource from the startup module
* Passing source to mw.loader.register from startup module
* Some new static helper methods
Use:
* By default nothing should change in core, all modules simply default to 'local'. This info originates from the getSource()-method of the ResourceLoaderModule class, which is inherited to all core ResourceLoaderModule-implementations (none override it)
* Third-party users and/or extensions can create new classes extending ResourceLoaderModule, re-implementing the getSource-method to return something else.
Basic example:
$wgResourceLoaderSources['mywiki'] = array( 'loadScript' => 'http://example.org/w/load.php' );
class MyCentralWikiModule extends ResourceLoaderModule {
function getSource(){
return 'mywiki';
}
}
$wgResourceModules['cool.stuff'] => array( 'class' => 'MyCentralWikiModule' );
More complicated example
// imagine some stuff with a ForeignGadgetRepo class, putting stuff in $wgResourceLoaderSources in the __construct() method
class ForeignGadgetRepoGadget extends ResourceLoaderModule {
function getSource(){
return $this->source;
}
}
Loading:
Loading is completely transparent, stuff like $wgOut->addModules() or mw.loader.loader/using both take it as any other module and load from the right source accordingly.
--
This commit is part of the ResourceLoader 2 project.
This is possibly not perfect but seems to serve for a start; follows up on r91591 that adds JSMin+ to use it in some unit tests. May want to adjust some related bits.
- $wgResourceLoaderValidateJs on by default (can be disabled)
- when loading a JS file through ResourceLoaderFileModule or ResourceLoaderWikiModule, parse it using JSMinPlus's JSParser class. If the parser throws an exception, the JS code of the offending file will be replaced by a JS exception throw listing the file or page name, line number (in original form), and description of the error from the parser.
- parsing results are cached based on md5 of content to avoid re-parsing identical text
- for JS pages loaded via direct load.php request, the parse error is thrown and visible in the JS console/error log
Issues:
- the primary use case for this is when a single load.php request implements multiple modules via mw.loader.implement() -- the loader catches the exception and skips on to the next module (good) but doesn't re-throw the exception for the JS console. It does log to console if present, but it'll only show up as a regular debug message, not an error. This can suppress visibility of errors in a module that's loaded together with other modules (such as a gadget).
- have not done performance testing on the JSParser
- have not done thorough unit testing with the JSParser
Also rearranges the loading order a little bit such that only=messages comes before only=scripts, and config comes before everything except startup and jquery+mediawiki
Done by adding isKnownEmpty() to ResourceLoaderModule and overriding it to check for page existence in ResourceLoaderWikiModule. Needed to rearrange some code in OutputPage::makeResourceLoaderLink() to have the emptiness check and dropping of modules work properly. Also factored the page_touched check in ResourceLoaderWikiModule::getModifiedTime() out to a separate method (getTitleMtimes()) and moved in-object caching there as well, so getModifiedTime() and isKnownEmpty() share code and caching for their timestamp/existence checks.
This does not account for the case where e.g. a user has user CSS but no user JS: I had implemented this by checking for $context->getOnly() in getTitleMtimes(), but then realized it's not safe to do this in a function called by getModifiedTime(): it causes the timestamp list in the startup module to only take scripts in account for wiki modules, because the startup module has &only=scripts set
(Almost looks like it could all go into ResourceLoaderModule... But that uses a different version, seemingly, the only one. 3 other subclasses of ResourceLoaderModule implement the same version of getFlip as is moved into a parent class here... Seems daft to have a different version in the base abstract class... Minor oversight?)
Some documentation
TODO:
* Are there instances where we might want to restrict CSS as well as JS?
* Would a $wg config option and/or user preference and/or index.php GET parameter to limit inclusion be useful?
* Can we deprecate any of the existing $wg config options?
* What's going on with the duplicated code between OutputPage and SkinTemplate?
* Interpreted some Trevor-speak in the doc comment of ResourceLoader::preloadModuleInfo().
* Made setMsgBlobMtime() (called from preloadModuleInfo()) actually work, by making getMsgBlobMtime() use the cached blob times if they are available.
* Break long lines.
* Convert long or unnecessary ternary operator usages to if/else.
* Fixed excessively clever assignment expressions.
* Rename $cache to $cacheEntry.
* Removed unnecessary web invocation guards. Their perlish form was making me uncomfortable. BTW, unlike in Perl, die() is not a function, it's a special case in the PHP grammar which very roughly simulates the Perl syntax:
die "x"; // works
0 || die("x"); // works
0 || (die); // works
0 || (die "x"); // fail!