User:Evad37/Script modules
![]() | The following is a draft working towards a proposal for adoption as a Wikipedia policy, guideline, or process. The proposal must not be taken to represent consensus, but is still in development and under discussion, and has not yet reached the process of gathering consensus for adoption. Thus references or links to this page should not describe it as policy, guideline, nor yet even as a proposal. |
Script modules are bits of Javascript code intended to be easily reused by userscripts. Script modules are to be stored in the MediaiWiki: namespace, as subpages of MediaWiki:Script module. A list of all script modules will thus be available at Special:PrefixIndex/MediaWiki:Script module/.
The basics
A new on-by-default gadget would add the functions exportScriptModule and importScriptModule to the Window object.
Pseudo-namespace
Script modules are to be stored in the pseudo-namespace MediaWiki:Script module/. This means that only code approved by Interface Admins can be exported to other scripts. However, script module authors should not expect a full code review from an Interface Admin: they are only required to do basic security and privacy checks (but may do further checks at their discretion). Thus, script module code should only be considered slightly more secure or reliable than your average userscript.
Module authors wanting to add or update a script module should use an edit request, or post a request at the Interface administrators' noticeboard.
For script modules
Script modules use the syntax:
Window.exportScriptModule('
name
', IIFE
);
where name
is the name of script module (subpage name without the .js suffix), and IIFE
is an immediately invoked function expression which returns the function or object to be exported. All of the script module's code is to be placed within the IIFE.
For userscripts
Userscripts use the syntax:
Window.importScriptModule('
name
').then(function(localVar
) {
/* ... code using the script module's export ... */
});
where name
is the name of script module (subpage name without the .js suffix), and localVar
is a local variable name for whatever the script module exported.
Just like mw.loader, the importScriptModule function expresses the intent that a script module be loaded. A new network request is not actually sent if that script module has already been loaded by another script.
Worked example
Suppose you want some code to parse wikitext for templates, and return an array of nicely formatted objects like:
{
name: 'nameOfTemplate',
wikitext: '{{nameOfTemplate|foo|2=1+1=2|bar=qux}}',
parameters: [
{name: 1, value:'foo'},
{name:'2', value:'1+1=2'},
{name:'bar', value:'qux'}
]
}
The module code would stored at MediaWiki:Script module/templateParser.js and contain something like:
Window.exportScriptModule('templateParser', (function() {
/* ... helper functions omitted ... */
var templateParser = function(wikitext) {
/* ... lots of lines omitted ... */
return result;
};
return templateParser;
})());
A userscript would access the template parser using code like this:
importScriptModule('templateParser').then(function(tParser) {
// `tParser` is our local variable name for the module's `templateParser` function. We can choose any name we want.
// We can now use the code defined in that module, e.g.
var templateInfo = tParser('==See also==\n{{Commons category}}\n{{Portal-inline|JavaScript|size=tiny}}\n*[[JavaScript templating]]');
/* `templateInfo` now evaluates to:
[
{
name: 'Commons category',
wikitext: '{{Commons category}}',
parameters: []
},
{
name: 'Portal-inline',
wikitext: '{{Portal-inline|JavaScript|size=tiny}}',
parameters: [
{name: 1, value:'JavaScript'},
{name:'size', value:'tiny'}
]
}
]
*/
};
// `tParser` is not available out here. The scope is limited to the function within .then()
Loading multiple script modules
To load several modules, use $.when()
, e.g.
$.when(
importScriptModule('templateParser'),
importScriptModule('multiButtonConfirm')
).then(function(tParser, multiConfirm) {
/* ... code using these modules goes here ... */
});
Loading errors
If there is an error loading a script module, the promise returned by importScriptModule will be rejected with an error message. Access the error message using a "failFilter" function in .then() e.g.
importScriptModule('moduleWithAnError').then(
function(foo) {
/* ... code that would be executed, if not for the loading error ... */
}),
function(failMessage) {
/* Show an error message to the user. Perhaps using bubble notifications from mw.notify */
}
);
The code
The code for the script enabling the exportScriptModule and importScriptModule functions would be
Window.exportScriptModule = function(name, exported) {
Window.exportScriptModule[name] = exported;
};
Window.importScriptModule = function(module) {
return mw.loader.using('mediawiki.util').then(function() {
if ( Window.exportScriptModule[module] === undefined ) {
// Load module
return $.getScript(
'https://en.wikipedia.org/w/index.php?title=MediaWiki:Script module' +
mw.util.wikiUrlencode(module) +
'.js&action=raw&ctype=text/javascript'
);
}
// else module has already been loaded
return true;
})
.then(
function() {
if ( Window.exportScriptModule[module] === undefined ) {
throw new Error("Script module " + module + " did not export anything!");
}
return Window.exportScriptModule[module];
},
function() {
throw new Error("Failed to load script module " + module);
}
);
};
This should probably be an on-by-default gadget for registered users, as it needs to be loaded before any userscripts are loaded, but is only useful for registered users.
Demos
Here are some demonstrations, using Evad37's subpages instead of MediaWiki namespace.
- ScriptModule import/export code: User:Evad37/ScriptModules.js
- "hello" script module: User:Evad37/Module/hello.js
- "goodbye" script module: User:Evad37/Module/goodbye.js
- "error" script module: User:Evad37/Module/error.js
- Script using "hello" script module: User:Evad37/ScriptModulesDemo.js
- Script using "hello" and "goodbye" script modules: User:Evad37/ScriptModulesDemo2.js
- Script attempting to use "error" script module, and then logging the load error: User:Evad37/ScriptModulesDemo3.js
Add the following code to your Special:MyPage/common.js to see these demonstrations working:
$.getScript('https://en.wikipedia.org/w/index.php?title=User:Evad37/ScriptModules.js&action=raw&ctype=text/javascript')
.then(function() {
importScript('User:Evad37/ScriptModulesDemo.js');
importScript('User:Evad37/ScriptModulesDemo2.js');
importScript('User:Evad37/ScriptModulesDemo3.js');
});