ResourceLoader/Package files

Package files can be used with a ResourceLoader module to bundle multiple script files and data exports, accessible client-side via.

To enable this feature in a module, use the  property of a module descriptor. Traditional modules specify multiple script files with the  property, which blindly concatenates them as if they were a single script without ability to separately access them client-side.

Node.js compatibility
This feature was originally conceived as a way to make code written for Node.js easier to integrate with ResourceLoader. Libraries that contain multiple files that  each other, could be registered as a ResourceLoader module without modification.

Private export and import
The ability to export values from individual files and import them within a module, has also proved useful for modernising JS code in MediaWiki itself, by no longer needing to attach all classes to a public and global object.

Data and config bundling
The ability for a module to have multiple "files" that remain individually addressable, has also opened the door to support bundling of JSON files and virtual data exports from PHP (as JSON). The proposal to embed config variables in ResourceLoader modules, thus ended up being implemented as virtual files in the module bundle.

Before this feature existed, embedding config and data in modules was also possible, but required writing your own subclass of ResourceLoaderFileModule, an obscure technique that most developers didn't know about or felt uncomfortable using. Most instances of this technique in MediaWiki core have now been ported to use a virtual file instead.

A more common technique for exporting values from PHP was to export them with or through the   hook, as   keys. This is problematic for performance because:


 * Startup module config vars were exported on all page views for all users (wastes bandwidth cost), and need to be parsed and processed before your actual modules can begin to download (delays interaction), and were only cached for a short time (frequent re-download).
 * OutputPage config vars are in the  and block downloading of article text (delays visual rendering), and have to be processed before actual modules can start to begin downloading (delays interaction).

Exports bundled using Package files, on the other hand:


 * Only downloaded if they are needed, together with the code (saves bandwidth).
 * Only downloaded when they are needed (avoids rendering or interaction delays).


 * Benefit from longer caching times (only re-downloaded if they change, as part of the module).

How it works
A package file can either be JavaScript code (script), or a JSON blob (data). A file's type is inferred based on its extension ( or  ). Files can be real files from disk, or dynamic files generated by code. Dynamic files are commonly used to export the values of configuration settings or other data from the server, and in those cases they're typically named  or.

Every module has one main file. This is the first file listed, unless another file is explicitly designated as the main file. When the module loads, only the code in the main file is executed. The code in the other files is not executed unless and until it is invoked using.

To invoke a non-main file, use. The path must start with  or   to indicate that it's a file in the same module (as opposed to the name of another module), and the path must be relative to the current file. The file suffix  is required. The return value of   is the value that foo.js assigns to.

Gadgets
Since MediaWiki 1.38, Gadgets also support package files. For details refer to Package Gadgets and Extension:Gadgets.

Module definition
Files can be specified in the  property in the following ways:

Static file from the filesystem
The name under which it is made available to  can be overridden if needed, using a   alias:

Main module file
By default the first JavaScript file in the  array will be considered the module's main file (or "entry point").

To set this explicitly instead, use the  attribute. This indicates which of the files executes first and has the control to import other files:

Dynamic source file
The  option can be used to dynamically decide which file to relate to an exported name. Read more about what callbacks can do in the sections further down.

Custom content
Define a virtual JavaScript file, with the specified contents:

Define a virtual JSON file, whose contents will serialised as JSON. For example the below would export :

The same works from PHP as well (such as in MediaWiki core's Resources.php file, or from an extension hook):

Generated content
Define a virtual file whose contents is generated by a callback. For JS files, the callback should return a string. For JSON files, it can return anything that's JSON-serializable (typically an associative array).

The callback is executed in the context of a load.php request and cached as part of that module, so it can't know which user is logged-in or which page is being viewed. Instead, the result is computed once and re-used across different users and pages. If you did attempt to access a RequestContext or User in your callback, it would likely return.

The  and   do offer information that you can vary by, such as by site configuration, current skin, and current interface language.

The callback takes an optional, which is set to the value of a   key specified in packageFiles. This allows callbacks to be re-used for multiple purposes:

Common pitfall: using i18n messages in generated content callbacks
A common source of errors is trying to use  in these callbacks. Trying to do this results in a  error. Instead, you should use the  object passed in as the first parameter, and replace calls to   with. Similarly, you should not use, use   instead.

Config files
This is a shortcut for the common case of generating JSON that exports one or more MediaWiki configuration variables. The below defines virtual file whose contents are :

Note that this syntax uses config setting names as understood by  (e.g.  ), which are without the   prefix that global variables use (e.g.  ).

You can also use aliases to export configuration variables under different names: will result in.

If you need to do more advanced manipulation of config variables, use a callback as described above. In the callback, you can use e.g.  to get the value of a config setting.

Base path
Most modules using Package files set  to the common directory prefix of the files. This is done for convenience (not having to write  over and over), and to make dynamic files easier to deal with. Without a base path, a dynamic file called  would have to be accessed using , or it would have to be named   so it could be accessed using. With a base path, you get the best of both worlds.

Note that, if you set, you will also have to set   (for core) or   (for extensions) to match.

Incompatibility with
If a module uses the  property, it cannot use the   property. Defining a module that uses both properties will throw an exception.

Package files are also incompatible with  and. Defining these properties won't throw an exception, but they will be ignored. A way of defining language/skin-specific script files for modules using Package files has not yet been developed (and this blocks porting ResourceLoaderLanguageDataModule).

Example uses in real code

 * ProtectionForm: Move JS config var to RL packageFiles
 * Use packageFiles feature to replace special-purpose RL modules
 * RCFilters: Export config vars in the RL modules where possible (see also the revert and fix)
 * ScoreHooks: Bundle configuration vars into JS that needs it
 * WikibaseQualityConstraints: Use packageFiles for constraint suggestions
 * [ your patches here! ]

Basic example/illustration
Yup

JavaScript
In init.js: In thinglib/index.js: In thinglib/formatter.js:

Debugging
In debug mode, traditional file modules load each of their files in a separate request. This feature is partly unavailable to Package files. In debug mode, modules with Package files are still loaded in a separate request and without minification, but individual files within the same module are bundled together in that request. If you're having trouble finding a file or a piece of code using your browser's developer tools (for example, to place a breakpoint somewhere), you can use the following hack.

Suppose you're trying to find the file named  in the module named    from the example above, and want to place a breakpoint in it. You can do that as follows:


 * 1) In your browser console, run  . This will print something like   (in Chrome) or   (in Firefox).
 * 2) In Chrome, this will print something like  ; click on this text. In Firefox, this will print   followed by an arrow pointing up and to the right to three horizontal lines; click this icon.
 * 3) This will take you to the "Scripts" panel . If you see minified code, click the "pretty print" button (at the bottom left of the code pane, the icon looks like  ).
 * 4) In Chrome, you will now be looking at a line of code highlighted in yellow that looks like  . The code of   begins below this line. In Firefox, you won't be taken to this place automatically, you'll have to search for  to find it.

You can also access the exports of a file (i.e. the value it assigns to  and returned by  ) by running   in the console. Often this is a function or an object containing functions, which you can then access using steps 2-4 above.