Requirejs and Webjars workaround for relative path dependencies

Last modified by Eduard Moraru on 2021/03/18 11:28

cogRoutes Webjar resource requests in a way that is compatible with RequireJS relative path dependencies.
TypeSnippet
CategoryOther
Developed by

Eduard Moraru

Rating
0 Votes
LicenseGNU Lesser General Public License 2.1

Description

This is needed in order to avoid the current limitation of XWiki's webjar URL format that contains a "?" character and which causes RequireJS to stop looking for relatively specified module dependencies. This was a blocking issue for example when loading any CodeMirror mode/addon/etc since their dependencies are relatively specified and were not resolved by RequireJS.

Example Problem

The following usage...

  require(["$services.webjars.url('codemirror', 'mode/css/css')"], function() {...}

...returns the problematic URL for RequireJS to handle:

http://localhost:8080/xwiki/bin/webjars/resources/path?value=codemirror/5.1/mode/css/css.js

...where "css.js" would define its dependency as:

  define(["../../lib/codemirror"], function(CodeMirror) {...})

Solution/Hack

This extension defines a document that interprets anything specified in the URL after the page name to being an internal resource belonging to a webjar. This way, RequireJS is allowed to request whatever it likes (using its dependency locator algorithm) while we are also able to translate that request (using $services.webjars.url) and redirect it to whatever XWiki's webjar URL format currently accepts.

{{velocity}}
#set ($pathPrefix = "${doc.externalURL}/")
#set ($url = $request.requestURL)
#set ($resource = $stringtool.substringAfter($url, $pathPrefix))
#set ($webjarId = $stringtool.substringBefore($resource, '/'))
#set ($resource = $stringtool.substringAfter($resource, '/'))
#if ("$!webjarId" != '' && "$!resource" != '')
  $response.sendRedirect($services.webjars.url($webjarId, $resource))
#else
Routes Webjar resource requests in a way that is compatible with RequireJS relative path dependencies.
#end
{{/velocity}}

Usage

require(["$xwiki.getURL('<snippetSpaceName>.<snippetDocumentName>')/<webjarId>/<resourcePath>"], function(...) {...});

Example:

require(["$xwiki.getURL('SyntaxHighlighting.RequireJsRequestRouter')/codemirror/lib/codemirror",
        "$xwiki.getURL('SyntaxHighlighting.RequireJsRequestRouter')/codemirror/mode/css/css"
        ], function (CodeMirror) {
  console.log(CodeMirror);
});

Example using paths:

require.config({
  paths: {
    cm : "$xwiki.getURL('SyntaxHighlighting.RequireJSRequestRouter')/codemirror"
  }
});

require(["cm/lib/codemirror",
        "cm/mode/css/css"
        ], function (CodeMirror) {
  console.log(CodeMirror);
});

Note that we don`t specify the .js extension since our css.js (example) module has a dependency which is also specified without .js extension (general practice in AMD modules). Once you start adding extensions to your required dependencies, RequireJS' dependency locator algorithm considers you know what you are doing and expects you to have extensions specified everywhere, even in the dependencies of your (module) dependencies (etc.). We don`t want that here.

Get Connected