* 'dependencies': ['required.foo', 'bar.also', ...]
* 'group': 'somegroup', (or) null
* 'source': 'local', (or) 'anotherwiki'
- * 'skip': 'return !!window.Example', (or) null
+ * 'skip': 'return !!window.Example', (or) null, (or) boolean result of skip
* 'module': export Object
*
* // Set from execute() or mw.loader.state()
* 'state': 'registered', 'loaded', 'loading', 'ready', 'error', or 'missing'
*
* // Optionally added at run-time by mw.loader.implement()
- * 'skipped': true
* 'script': closure, array of urls, or string
* 'style': { ... } (see #execute)
* 'messages': { 'key': 'value', ... }
/**
* @private
- * @param {Array} modules List of module names
+ * @param {string[]} modules List of module names
* @return {string} Hash of concatenated version hashes.
*/
function getCombinedVersion( modules ) {
* execute the module or job now.
*
* @private
- * @param {Array} modules Names of modules to be checked
+ * @param {string[]} modules Names of modules to be checked
* @return {boolean} True if all modules are in state 'ready', false otherwise
*/
function allReady( modules ) {
* dependencies, such that later modules depend on earlier modules. The array
* contains the module names. If the array contains already some module names,
* this function appends its result to the pre-existing array.
- * @param {StringSet} [unresolved] Used to track the current dependency
- * chain, and to report loops in the dependency graph.
- * @throws {Error} If any unregistered module or a dependency loop is encountered
+ * @param {StringSet} [unresolved] Used to detect loops in the dependency graph.
+ * @throws {Error} If an unknown module or a circular dependency is encountered
*/
function sortDependencies( module, resolved, unresolved ) {
- var i, deps, skip;
+ var i, skip, deps;
if ( !( module in registry ) ) {
- throw new Error( 'Unknown dependency: ' + module );
+ throw new Error( 'Unknown module: ' + module );
}
- if ( registry[ module ].skip !== null ) {
+ if ( typeof registry[ module ].skip === 'string' ) {
// eslint-disable-next-line no-new-func
- skip = new Function( registry[ module ].skip );
- registry[ module ].skip = null;
- if ( skip() ) {
- registry[ module ].skipped = true;
+ skip = ( new Function( registry[ module ].skip )() );
+ registry[ module ].skip = !!skip;
+ if ( skip ) {
registry[ module ].dependencies = [];
setAndPropagate( module, 'ready' );
return;
}
}
- if ( resolved.indexOf( module ) !== -1 ) {
- // Module already resolved; nothing to do
- return;
- }
// Create unresolved if not passed in
if ( !unresolved ) {
unresolved = new StringSet();
}
- // Add base modules
- if ( baseModules.indexOf( module ) === -1 ) {
- for ( i = 0; i < baseModules.length; i++ ) {
- if ( resolved.indexOf( baseModules[ i ] ) === -1 ) {
- resolved.push( baseModules[ i ] );
- }
- }
- }
-
- // Tracks down dependencies
+ // Track down dependencies
deps = registry[ module ].dependencies;
unresolved.add( module );
for ( i = 0; i < deps.length; i++ ) {
sortDependencies( deps[ i ], resolved, unresolved );
}
}
+
resolved.push( module );
}
* @throws {Error} If an unregistered module or a dependency loop is encountered
*/
function resolve( modules ) {
- var resolved = [],
+ // Always load base modules
+ var resolved = baseModules.slice(),
i = 0;
for ( ; i < modules.length; i++ ) {
sortDependencies( modules[ i ], resolved );
*/
function resolveStubbornly( modules ) {
var saved,
- resolved = [],
+ // Always load base modules
+ resolved = baseModules.slice(),
i = 0;
for ( ; i < modules.length; i++ ) {
saved = resolved.slice();
* to a query string of the form `foo.bar,baz|bar.baz,quux`.
*
* See `ResourceLoader::makePackedModulesString()` in PHP, of which this is a port.
- * On the server, unpacking is done by `ResourceLoaderContext::expandModuleNames()`.
+ * On the server, unpacking is done by `ResourceLoader::expandModuleNames()`.
*
* Note: This is only half of the logic, the other half has to be in #batchRequest(),
* because its implementation needs to keep track of potential string size in order
* "text/javascript"; if no type is provided, text/javascript is assumed.
*/
load: function ( modules, type ) {
- var filtered, l;
+ var l;
// Allow calling with a url or single dependency as a string
if ( typeof modules === 'string' ) {
modules = [ modules ];
}
- // Filter out top-level modules that are unknown or failed to load before.
- filtered = modules.filter( function ( module ) {
- var state = mw.loader.getState( module );
- return state !== 'error' && state !== 'missing';
- } );
- // Resolve remaining list using the known dependency tree.
- // This also filters out modules with unknown dependencies. (T36853)
- filtered = resolveStubbornly( filtered );
- // Some modules are not yet ready, add to module load queue.
- enqueue( filtered, undefined, undefined );
+ // Resolve modules into flat list for internal queuing.
+ // This also filters out unknown modules and modules with
+ // unknown dependencies, allowing the rest to continue. (T36853)
+ enqueue( resolveStubbornly( modules ), undefined, undefined );
},
/**