Background
When defining a module, we always set a pair of base paths. A localBasePath for where files are on disk, and remoteBasePath for where those files can be accessed from the web. For example, /var/www/mediawiki/resources/examples and /w/resources/example, usually expressed as follows:
'localBasePath' => MW_INSTALL_PATH . '/resources/src/example', 'remoteBasePath' => "$wgResourceBasePath/resources/src/example",
Or for extensions, where ExtensionProcessor takes care of prepending __DIR__, and FileModule::extractBasePaths takes care of prepending $wgExtensionAssetsPath (/w/extensions/)
"ResourceModules": { "ext.wikimediaEvents": { "localBasePath": "modules/ext.wikimediaEvents", "remoteExtPath": "WikimediaEvents/modules/ext.wikimediaEvents",
- localBasePath is the path on disk to the directory relative to the extension.json file.
- remoteBasePath is the path to the directory as it is served over HTTP, e.g. /w/extensions/FooBar/resources/ext.foo.
- remoteExtPath option is a shortcut for remoteBasePath that injects/prepends site configuration wgExtensionAssetsPath.
Idea (2023)
The remote path is the one developers most often set incorrectly because it is least exposed. The majority of file references are flattened (e.g. CSS and JS), embedded (e.g. @embed images), or transformed (e.g. RL images served from load.php).
It is only when you reference an image in CSS without embed and without transformation, or when using legacy debug mode with debug=1, or debug with source maps (T47514) that the remote paths are exposed.
Combine this with the fact that it is possible to determine the "right" remote path based on site configuration, I think this would benefit from automation.
We've done this before already with OutputPage::transformResourcePath, which already shows that localBasePath suffices to figure out the web-based URL path.
Broadly, this would mean three good things:
- The following options become redundant, thus simplying the API for developers:
- remoteBasePath,
- remoteExtPath,
- remoteSkinPath.
- There is no longer a difference in how modules are defined in core vs in an extension. This makes understanding and skills more transferrable and maximises utility of documentation and their search/discovery by not having multiple ways to express the same concept.
- There is no longer the awkward requirement for extensions to internally hardcode and rely on the name of their installed subdirectory. (This could be solved through ExtensionProcessor automation if we wanted, but thusfar we never did. If this task ends up infeasible, I'd probably suggest that instead. It would mean we let ExtensionProcessor fill in the name of the extension directory as well, and thus the two strings become identical in extension.json, allowing us to remove remoteExtPath from extension.json, even if we keep remoteBasePath as concept inside of ResourceLoader.)
There is no immediate need to mass-remove these, as they simply become ignored (akin to when position: "top" was removed, T107399).
Previous idea (2021)
We could start by making remoteBasePath (and remoteExtPath)optional, and have ExtensionProcessor fill in the default based on the extension name (basename of $dir in ExtensionProcessor) plus localBasePath. However, I'd like to explore the possibility of taking this one step further and deeper into ResourceLoader and actually drive towards automating the URL formation in a more robust and verified way.
In OutputPage::transformResourcePath we currently do the inverse of what we need. We try to map a remote path to a file, by comparing it against the possible assets sources (wgExtensionAssetsPath, wgStylePath, wgUploadPath, wgResourceBasePath, etc.) and then mapping it to their on-disk equivalent.
Perhaps it would be feasible to do something similar to map a file path to where it is exposed over HTTP without needing to be told where it is. Things to consider include:
- non-default wgExtensionAssetsPath.
- non-overlapping wgExtensionAssetsPath and wgStylePath.
- non-overlapping wgResourceBasePath and wgScriptPath (e.g. wgResourceBasePath with a different hostname).
- random other file that has no mapping to any of these (e.g. outside $IP, or in $IP but forbidden through its normal wgScriptPath path, such as vendor). For these we'd presumably support FilePath objects like we do today.