resourceloader: Implement modern module loading (2/2)
* Send 'module' and 'require' parameters to module closures.
This depends on Ia925844cc22f143 being deployed one cycle earlier.
* Patch Moment and OOjs to ensure these libraries continue to expose
their module as globals as well. AMD/UMD-compatible libraries
only expose a global *OR* an export, not both. We need both
for back-compat.
* Update pluralRuleParser to make use of module export to allow
usage via require().
To test, check out the patch and run:
> mw.loader.load('moment');
> mw.loader.require('moment')()
> mw.loader.require('moment')('2011-04-01').fromNow()
Bug: T108655
Change-Id: Idbd054880ee70d659ec760aef8fcb38d0704a394
This commit is contained in:
parent
8cb8af442a
commit
bc4e07b6f6
13 changed files with 55 additions and 7 deletions
|
|
@ -1098,7 +1098,7 @@ MESSAGE;
|
|||
$scripts = self::filter( 'minify-js', $scripts );
|
||||
}
|
||||
} else {
|
||||
$scripts = new XmlJsCode( "function ( $, jQuery ) {\n{$scripts}\n}" );
|
||||
$scripts = new XmlJsCode( "function ( $, jQuery, require, module ) {\n{$scripts}\n}" );
|
||||
}
|
||||
} elseif ( !is_array( $scripts ) ) {
|
||||
throw new MWException( 'Invalid scripts error. Array of URLs or string of code expected.' );
|
||||
|
|
|
|||
|
|
@ -734,6 +734,7 @@ return [
|
|||
'moment' => [
|
||||
'scripts' => [
|
||||
'resources/lib/moment/moment.js',
|
||||
'resources/src/moment-global.js',
|
||||
'resources/src/moment-local-dmy.js',
|
||||
],
|
||||
'languageScripts' => [
|
||||
|
|
@ -2278,6 +2279,7 @@ return [
|
|||
'oojs' => [
|
||||
'scripts' => [
|
||||
'resources/lib/oojs/oojs.jquery.js',
|
||||
'resources/src/oojs-global.js',
|
||||
],
|
||||
'targets' => [ 'desktop', 'mobile' ],
|
||||
'dependencies' => [
|
||||
|
|
|
|||
|
|
@ -591,5 +591,6 @@ function pluralRuleParser(rule, number) {
|
|||
|
||||
/* pluralRuleParser ends here */
|
||||
mw.libs.pluralRuleParser = pluralRuleParser;
|
||||
module.exports = pluralRuleParser;
|
||||
|
||||
} )( mediaWiki );
|
||||
|
|
|
|||
|
|
@ -1189,11 +1189,18 @@
|
|||
* @param {string} [moduleName] Name of currently executing module
|
||||
* @return {jQuery.Promise}
|
||||
*/
|
||||
function queueModuleScript( src ) {
|
||||
function queueModuleScript( src, moduleName ) {
|
||||
var r = $.Deferred();
|
||||
|
||||
pendingRequests.push( function () {
|
||||
if ( moduleName && hasOwn.call( registry, moduleName ) ) {
|
||||
window.require = mw.loader.require;
|
||||
window.module = registry[ moduleName ].module;
|
||||
}
|
||||
addScript( src ).always( function () {
|
||||
// Clear environment
|
||||
delete window.require;
|
||||
delete window.module;
|
||||
r.resolve();
|
||||
|
||||
// Start the next one (if any)
|
||||
|
|
|
|||
2
resources/src/moment-global.js
Normal file
2
resources/src/moment-global.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Back-compat: Export module as global
|
||||
window.moment = module.exports;
|
||||
2
resources/src/oojs-global.js
Normal file
2
resources/src/oojs-global.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
// Back-compat: Export module as global
|
||||
window.OO = module.exports;
|
||||
|
|
@ -169,7 +169,7 @@ class OutputPageTest extends MediaWikiTestCase {
|
|||
[
|
||||
[ 'test.quux', ResourceLoaderModule::TYPE_COMBINED ],
|
||||
"<script>(window.RLQ=window.RLQ||[]).push(function(){"
|
||||
. "mw.loader.implement(\"test.quux\",function($,jQuery){"
|
||||
. "mw.loader.implement(\"test.quux\",function($,jQuery,require,module){"
|
||||
. "mw.test.baz({token:123});},{\"css\":[\".mw-icon{transition:none}"
|
||||
. "\"]});});</script>"
|
||||
],
|
||||
|
|
|
|||
|
|
@ -188,7 +188,7 @@ class ResourceLoaderTest extends ResourceLoaderTestCase {
|
|||
'messages' => [ 'example' => '' ],
|
||||
'templates' => [],
|
||||
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
||||
mw.example();
|
||||
}, {
|
||||
"css": [
|
||||
|
|
@ -207,7 +207,7 @@ mw.example();
|
|||
'messages' => new XmlJsCode( '{}' ),
|
||||
'templates' => [],
|
||||
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
||||
mw.example();
|
||||
} );',
|
||||
] ],
|
||||
|
|
@ -235,7 +235,7 @@ mw.example();
|
|||
'messages' => [ 'example' => '' ],
|
||||
'templates' => [],
|
||||
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
||||
mw.example();
|
||||
}, {}, {
|
||||
"example": ""
|
||||
|
|
@ -250,7 +250,7 @@ mw.example();
|
|||
'messages' => new XmlJsCode( '{}' ),
|
||||
'templates' => [ 'example.html' => '' ],
|
||||
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery ) {
|
||||
'expected' => 'mw.loader.implement( "test.example", function ( $, jQuery, require, module ) {
|
||||
mw.example();
|
||||
}, {}, {}, {
|
||||
"example.html": ""
|
||||
|
|
|
|||
|
|
@ -8,7 +8,15 @@ return [
|
|||
|
||||
'test.sinonjs' => [
|
||||
'scripts' => [
|
||||
'tests/qunit/suites/resources/test.sinonjs/index.js',
|
||||
'resources/lib/sinonjs/sinon-1.17.3.js',
|
||||
// We want tests to work in IE, but can't include this as it
|
||||
// will break the placeholders in Sinon because the hack it uses
|
||||
// to hijack IE globals relies on running in the global scope
|
||||
// and in ResourceLoader this won't be running in the global scope.
|
||||
// Including it results (among other things) in sandboxed timers
|
||||
// being broken due to Date inheritance being undefined.
|
||||
// 'resources/lib/sinonjs/sinon-ie-1.15.4.js',
|
||||
],
|
||||
'targets' => [ 'desktop', 'mobile' ],
|
||||
],
|
||||
|
|
|
|||
1
tests/qunit/data/defineCallMwLoaderTestCallback.js
Normal file
1
tests/qunit/data/defineCallMwLoaderTestCallback.js
Normal file
|
|
@ -0,0 +1 @@
|
|||
module.exports = 'Define worked.';
|
||||
2
tests/qunit/data/requireCallMwLoaderTestCallback.js
Normal file
2
tests/qunit/data/requireCallMwLoaderTestCallback.js
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
var x = require( 'test.require.define' );
|
||||
module.exports = 'Require worked.' + x;
|
||||
|
|
@ -1127,4 +1127,24 @@
|
|||
}, /is not loaded/, 'Requesting non-existent modules throws error.' );
|
||||
} );
|
||||
|
||||
QUnit.asyncTest( 'mw.loader require in debug mode', 1, function ( assert ) {
|
||||
var path = mw.config.get( 'wgScriptPath' );
|
||||
mw.loader.register( [
|
||||
[ 'test.require.define', '0' ],
|
||||
[ 'test.require.callback', '0', [ 'test.require.define' ] ]
|
||||
] );
|
||||
mw.loader.implement( 'test.require.callback', [ QUnit.fixurl( path + '/tests/qunit/data/requireCallMwLoaderTestCallback.js' ) ] );
|
||||
mw.loader.implement( 'test.require.define', [ QUnit.fixurl( path + '/tests/qunit/data/defineCallMwLoaderTestCallback.js' ) ] );
|
||||
|
||||
mw.loader.using( 'test.require.callback', function () {
|
||||
QUnit.start();
|
||||
var exported = mw.loader.require( 'test.require.callback' );
|
||||
assert.strictEqual( exported, 'Require worked.Define worked.',
|
||||
'module.exports worked in debug mode' );
|
||||
}, function () {
|
||||
QUnit.start();
|
||||
assert.ok( false, 'Error callback fired while loader.using "test.require.callback" module' );
|
||||
} );
|
||||
} );
|
||||
|
||||
}( mediaWiki, jQuery ) );
|
||||
|
|
|
|||
3
tests/qunit/suites/resources/test.sinonjs/index.js
Normal file
3
tests/qunit/suites/resources/test.sinonjs/index.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
// Hack: Disable 'module.exports' from ResourceLoader
|
||||
// (Otherwise Sinon assumes context as Node.js instead of a browser)
|
||||
module.exports = null;
|
||||
Loading…
Reference in a new issue