RequireJS relative paths breaks some libraries submodules

I am finishing a JS view and found an error when testing it on the server. I believe it is generated by KNIME when importing the libraries with RequireJS.

The view is using CodeMirror (CodeMirror 5 User Manual) to produce some text editors for some scripts in textareas. CodeMirror includes some extra JS sources for syntax highlighting, themes and other plugins. In this view so far I used two syntaxes: R and Markdown. The structure of the library is something like:

  • lib/ folder with JS and CSS
  • mode/ folder with syntax highlighters

There are more folders but for my needs now these are enough. Anyway, CodeMirror does not work when the node is in a wrapped metanode (to be used in the server) and produces the error:

Failed to load resource: net::ERR_FILE_NOT_FOUND
require.js:143 Uncaught Error: Script error for "codemirror/../../lib/codemirror", needed by: codemirror/r
http://requirejs.org/docs/errors.html#scripterror
    at makeError (require.js:168)
    at HTMLScriptElement.onScriptError (require.js:1738)

The same JS view does work when the node is not wrapped. The configuration in the plugin’s feature.xml is:

<webResourceBundle debug="false" name="codemirror" usesDefine="true" version="5.9.2" webResourceBundleID="codemirror">    
  <webResource relativePathSource="js-lib/codemirror/" relativePathTarget="codemirror"</webResource>
  <importResource relativePath="codemirror/codemirror.js" type="JAVASCRIPT" />
  <importResource relativePath="codemirror/codemirror.css" type="CSS" />
  <importResource relativePath="codemirror/r.js" type="JAVASCRIPT" />
  <!-- <importResource relativePath="codemirror/markdown.js" type="JAVASCRIPT" /> -->
</webResourceBundle>

In CodeMirror’s user manual there is a warning about RequireJS:

Do not use RequireJS’ paths option to configure the path to CodeMirror, since it will break loading submodules through relative paths. Use the packages configuration option instead, as in:

require.config({
  packages: [{
    name: "codemirror",
    location: "../path/to/codemirror",
    main: "lib/codemirror"
  }]
});

Thus, my guess is the relativePaths in the webResourceBundle are breaking somehow the submodules. It is just a guess, I have not yet checked the source code. Is there any way to work around it? Or could it be fixed?

Thanks,
Miguel

Hi Miguel,

I guess since the loading of codemirror seems to be a special case you could try to not declare codemirror as a dependency of the view, but rather the ‘knimeserviceConditionalLoad_1.0’ and then in the view itself call knimeService.loadConditionally(paths, success, failure, config). Here you would pass as paths an array of identifiers (what you have defined as names in the config), a callback method for success and failure and an optional requireJS config object. This config object you can create as is described in the codemirror documentation and then you should be good to go.

Hope this helps.

Cheers,
Christian

2 Likes

Hi Christian,

Lots of thanks. I am a bit busy and could not get to it until yesterday. I have not managed yet to load the library but with your help I made some advances.

First, like you said, I am not including the library as a dependency in the plugin configuration. Just the directory:

<webResource relativePathSource="js-lib/codemirror-5.49.2" relativePathTarget="codemirror-5.49.2"></webResource>

I followed the project org.knime.js.testing where loadConditionally is used in https://github.com/knime/knime-js-base/blob/c0a1de78a19a6eaff86405c000f784669666b5e2/org.knime.js.testing/js-src/org/knime/js/node/minimalRequestHandler/requestHandler.js.

In the view the library is loaded with:

// Load CodeMirror
let codemirror_config = {
    packages: [{
        name: "codemirror",
        location: "codemirror-5.49.2",
        main: "lib/codemirror"
    }
    // ,{
    //   name: "codemirror_R",
    //   location: "codemirror-5.49.2",
    //   main: "mode/r/r"
    // }
]};
knimeService.loadConditionally(["codemirror"],
    (arg) => console.log("knimeService installed " + arg),
    (err) => console.log("knimeService failed to install " + err),
    codemirror_config);
window.CodeMirror = codemirror;

This is just loading the base library. I am trying to avoid the errors related to the R mode for now. The library cannot be used in the view yet the success callback is called and the library appears in the Chrome debugger.

image

I have very little time left for this task and will try with RequireJS. By the way, I did not find any use of loadConditionally with the four arguments passed. I only found it used with a single path.

Best,
Miguel

Update. The view is now working with CodeMirror modes!! I am just updating in case it helps someone. The library was already loaded properly but the require/knimeService call was invoked after the page was loaded and thus the library was not available then.

The require configuration was fine. Only the main js needs to be configured.

let require_config = {
    packages: [{
    name: "codemirror",
    location: "codemirror/",
    main: "lib/codemirror"
    }]
};

Then the code mirrors are created when the UI is already created.

let res = knimeService.loadConditionally(
    ["codemirror", "codemirror/mode/r/r", "codemirror/mode/python/python",
    "codemirror/mode/markdown/markdown"],
    (arg) => {
        let CodeMirror = arg[0];
        _modelCodeMirror = CodeMirror.fromTextArea(document.getElementById("modelScriptArea"), {
            lineNumbers: true,
            lineWrapping: true,
            extraKeys: { 'Ctrl-Space': 'autocomplete' },
            mode: { 'name': "text/x-rsrc" }
        });
        _visualizationCodeMirror = CodeMirror.fromTextArea(document.getElementById("visualizationScriptArea"), {
        lineNumbers: true,
        lineWrapping: true,
        extraKeys: { 'Ctrl-Space': 'autocomplete' },
        mode: { 'name': "text/x-python" }
        });
        _readmeCodeMirror = CodeMirror.fromTextArea(document.getElementById("readmeArea"), {
        lineNumbers: true,
        lineWrapping: true,
        extraKeys: { 'Ctrl-Space': 'autocomplete' },
        mode: { 'name': "text/x-markdown" }
        });

        _modelCodeMirror.on("blur", () => { _modelCodeMirror.focus(); });
        _visualizationCodeMirror.on("blur", () => { _visualizationCodeMirror.focus(); });
        _readmeCodeMirror.on("blur", () =>{ _readmeCodeMirror.focus(); });
    },
    (err) => console.log("knimeService failed to install " + err),
    require_config);

Here the modes and addons can be added as long as they main CodeMirror JS is configured. And the CSS file needs to be configured in the plugin.xml as RequireJS does not load it.

Thanks a lot Christian.
Miguel

3 Likes